在linux C编程时,会碰到程序异常退出的情况,除了比较经典的段错误、内存溢出之外,本文说一种文件连接过多而导致程序异常退出的情况。
简述一下我遇到的情况,在煲机一段时间之后(大概18小时),进程退出。通过gdb调试和及时保存内存CPU使用情况,基本可以排除内存泄漏和段错误的原因。然后开始排查是否是文件连接过多的原因,即fopen文件没有相应close。
1 ulimit -a 查看最大打开文件数
$ulimit -a
通过上面命令可以在linux系统获取最大打开文件数。
标红位置为最大打开文件数,如果超过该文件数,程序就可能会出问题。
2 /proc/$pid/fd 查看进程打开的文件连接
通过打开/proc/$pid/fd,可以显示相应pid的进程的文件连接数(ps 命令可以看进程的pid为多少)。
打开/proc/$pid/fd ls,显示如下:
由上图所示,明显文件连接数过多。
ls -l查看详细信息:
由上图可以知道,该程序应该是打开了过多的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分钟会出现问题。
更改之后,运行程序:
由上图可见,文件打开的很快的增加上来,最后导致程序崩溃。时间大概为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是可查看系统级别的文件数显示
ulimit -n 10240可以修改文件数限制,如果希望永久修改则需修改/etc/security/limits.conf