LINUX C 程序异常退出---文件连接数过多

在linux C编程时,会碰到程序异常退出的情况,除了比较经典的段错误、内存溢出之外,本文说一种文件连接过多而导致程序异常退出的情况。

简述一下我遇到的情况,在煲机一段时间之后(大概18小时),进程退出。通过gdb调试和及时保存内存CPU使用情况,基本可以排除内存泄漏和段错误的原因。然后开始排查是否是文件连接过多的原因,即fopen文件没有相应close。

1 ulimit -a 查看最大打开文件数

$ulimit -a

通过上面命令可以在linux系统获取最大打开文件数。
LINUX C 程序异常退出---文件连接数过多_第1张图片
标红位置为最大打开文件数,如果超过该文件数,程序就可能会出问题。
2 /proc/$pid/fd 查看进程打开的文件连接

通过打开/proc/$pid/fd,可以显示相应pid的进程的文件连接数(ps 命令可以看进程的pid为多少)。

打开/proc/$pid/fd ls,显示如下:
LINUX C 程序异常退出---文件连接数过多_第2张图片
由上图所示,明显文件连接数过多。
ls -l查看详细信息:
LINUX C 程序异常退出---文件连接数过多_第3张图片
由上图可以知道,该程序应该是打开了过多的socket,而没有关闭。
NOTE:如果是打开了别的文件过多,就不用看下面的介绍了,去排查是不是打开了过多次的那个文件便可。

3 socket获取mac地址

通过上面的分析,可知是socket的问题,在代码中搜索socket。
通过排查,最终定位到了以下这段代码。是用来获取设备mac地址的。

void get_MacAddr(char *Mac, S32 MacLen, char *EthType)
{
    if(!Mac || !EthType)
    {
        return ;
    }

    S32 s32Macfd = 0;
    struct ifreq Macreq;

    s32Macfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(s32Macfd < 0)
    {
        printf("build socket fail\n");
        return ;
    }

    strcpy(Macreq.ifr_name, EthType);

    if(ioctl(s32Macfd, SIOCGIFHWADDR, &Macreq) < 0)
    {
        printf("ioctl error\n");
        return ;
    }

    snprintf(Mac,  MacLen,  "%02x:%02x:%02x:%02x:%02x:%02x",
        Macreq.ifr_hwaddr.sa_data[0],Macreq.ifr_hwaddr.sa_data[1],
        Macreq.ifr_hwaddr.sa_data[2],Macreq.ifr_hwaddr.sa_data[3],
        Macreq.ifr_hwaddr.sa_data[4],Macreq.ifr_hwaddr.sa_data[5]);
}

可以观察到该段代码没有close。通过参考该代码,http://www.cnblogs.com/kernel-style/archive/2013/02/01/2888598.html。
猜测很有可能是这段代码的原因。
  这段代码每40秒就会被引用一次,(是为了给服务器上报本设备的状态,即心跳),而每一次都是socket打开之后并没有关闭,猜测有可能是这个问题。
  尝试将上报状态的时间由40秒改为了0.1秒,如果仍然出问题,应该是18h/400,即大约2.7分钟会出现问题。
更改之后,运行程序:
LINUX C 程序异常退出---文件连接数过多_第4张图片
LINUX C 程序异常退出---文件连接数过多_第5张图片

由上图可见,文件打开的很快的增加上来,最后导致程序崩溃。时间大概为3分钟。跟之前判断基本吻合。

将代码改为:

void get_MacAddr(char *Mac, S32 MacLen, char *EthType)
{
    if(!Mac || !EthType)
    {
        return ;
    }

    S32 s32Macfd = 0;
    struct ifreq Macreq;

    s32Macfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(s32Macfd < 0)
    {
        printf("build socket fail\n");
        return ;
    }

    strcpy(Macreq.ifr_name, EthType);

    if(ioctl(s32Macfd, SIOCGIFHWADDR, &Macreq) < 0)
    {
        printf("ioctl error\n");
        return ;
    }
    close(s32Macfd);///*修改了此处*/

    snprintf(Mac,  MacLen,  "%02x:%02x:%02x:%02x:%02x:%02x",
        Macreq.ifr_hwaddr.sa_data[0],Macreq.ifr_hwaddr.sa_data[1],
        Macreq.ifr_hwaddr.sa_data[2],Macreq.ifr_hwaddr.sa_data[3],
        Macreq.ifr_hwaddr.sa_data[4],Macreq.ifr_hwaddr.sa_data[5]);
}

再重新运行,没有复现上述问题。

4 延伸配置

ulimit -n是用户级的文件数限制
sysctl -a是可查看系统级别的文件数显示
LINUX C 程序异常退出---文件连接数过多_第6张图片

ulimit -n 10240可以修改文件数限制,如果希望永久修改则需修改/etc/security/limits.conf
LINUX C 程序异常退出---文件连接数过多_第7张图片

你可能感兴趣的:(Liunx,/,debug,异常,c语言,linux)