【实战指南】记一次定位fd泄漏问题

简介: 本文记录了一次文件描述符(fd)泄漏问题的排查过程。在项目压测中,进程因打开过多文件导致fd资源耗尽,最终无法创建新文件。通过分析错误码、查看/proc/pid/fd路径下的文件句柄信息,定位到临时文件未正确关闭的问题根源,并修复代码中遗漏的close调用。同时总结了Linux下进程资源限制的相关知识点,强调开发中应关注资源使用情况,避免类似问题发生。

记一次定位fd泄漏问题

  最近项目压测遇到一个fd创建失败的问题,仔细一查发现进程竟然使用了1027个句柄,导致fd资源耗尽无法再创建,记录一下踩坑全过程。

问题背景

  某个功能模块需要创建唯一的临时文件用于处理日常事件,当事件比较频繁时,就会频繁创建文件。 模拟示例如下:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
void createFileName()
{
    int fd;
    char temp_file[]="/tmp/tmp_XXXXXX";
    
    /*Creat a temp file.*/
    if((fd = mkstemp(temp_file)) == -1){
        printf("Create %s failed. errno = %d.\n", temp_file, errno);
    }
    unlink(temp_file);
}
int main(int argc, char *argv[])
{
    for (int i = 0; i < 2000; i++)
    {
        createFileName();
    }
    while(1);
    return 0;
}

当上述代码执行时,就会报错:

...
Create /tmp/tmp_sb1Ub1 failed. errno = 24.
Create /tmp/tmp_Zd8VM0 failed. errno = 24.
Create /tmp/tmp_veVqq3 failed. errno = 24.
Create /tmp/tmp_HFGEU2 failed. errno = 24.
Create /tmp/tmp_Wp2wO1 failed. errno = 24.
...

这里的示例代码比较简单,一眼就能看出原因。假设这段代码藏在庞大工程的小角落里,分析一下如何定位问题。

问题分析

① 通过系统接口返回的错误码,分析问题。

查看Linux错误码表,可知当前进程open 太多文件。

errno: 24       Too many open files

② 查看当前进程打开了哪些文件

$ ps -ef | grep a.out
dx         14101   13040 90 07:02 pts/3    00:00:14 ./a.out
$ ls /proc/14101/fd | wc -l
1027
$ ls -al /proc/14101/fd
...
lrwx------ 1 dx dx 64 Mar 29 07:07 989 -> '/tmp/tmp_mDhS7e (deleted)'
lrwx------ 1 dx dx 64 Mar 29 07:07 99 -> '/tmp/tmp_2vNCsd (deleted)'
lrwx------ 1 dx dx 64 Mar 29 07:07 990 -> '/tmp/tmp_RMAFDf (deleted)'
...

/proc/pid/fd路径下存着当前占用的fd资源,每个fd都会指向对应的文件名。由此可以定位到哪些路径大量占用。

通过终端的输出大致可以猜出/tmp/路径下大量的文件已删除,引用未释放。对应代码即unlink,没有close

对应修改,加上close即可:

void createFileName()
{
    int fd;
    char temp_file[]="/tmp/tmp_XXXXXX";
    
    /*Creat a temp file.*/
    if((fd = mkstemp(temp_file)) == -1){
        printf("Create %s failed. errno = %d.\n", temp_file, errno);
    }
    close(fd); // 及时关闭mkstemp fd
    unlink(temp_file);
}

总结

在Linux中,给每个进程分配的资源是有限制的,为的是避免某个进程把Linux资源耗尽,导致其他进程异常。同样在开发中要时刻关注这些资源是否异常。

在Linux中,进程资源限制主要有如下几项(括号内查看限制值):

  1. 进程打开的文件数量限制(ulimit -n):限制进程能够同时打开的文件数量,可以在/etc/security/limits.conf文件中设置。
  2. 进程内存使用限制(ulimit -m):限制进程在虚拟内存中使用的最大字节数,可以在/etc/security/limits.conf文件中设置。
  3. 进程CPU时间限制(ulimit -t):限制进程可以使用的CPU时间,可以在/etc/security/limits.conf文件中设置。
  4. 进程堆栈大小限制(ulimit -s):限制进程堆栈的大小,可以在/etc/security/limits.conf文件中设置。
  5. 进程可打开文件的最大大小限制(ulimit -f):限制进程可以创建的最大文件大小,可以在/etc/security/limits.conf文件中设置。
  6. 进程最大用户进程数限制(ulimit -u):限制进程可以创建的最大用户进程数,可以在/etc/security/limits.conf文件中设置。
  7. 进程最大打开文件描述符数限制(ulimit -Hn):限制进程可以同时打开的文件描述符数,可以在/etc/security/limits.conf文件中设置。
  8. 进程最大线程数限制(ulimit -i):限制进程可以创建的最大线程数,可以在/etc/security/limits.conf文件中设置。

以上限制都可以通过ulimit命令来查看和修改,也可以通过修改/etc/security/limits.conf文件来永久修改。

相关文章
|
人工智能 监控 供应链
AI技术创业有哪些机会?
本文探讨了AI技术创业的多个机会,包括提供行业解决方案、开发智能产品和服务以及教育和培训,为创业者在医疗保健、金融服务、零售、教育等多个领域提供了丰富的机遇。
1043 2
|
存储 缓存 网络协议
深入理解Linux网络——内核是如何接收到网络包的
一、相关实际问题 RingBuffer是什么,为什么会丢包 网络相关的硬中断、软中断是什么 Linux里的ksoftirqd内核线程是干什么
|
缓存 编译器 Shell
【实战指南】 CMake搭建编译环境总结
本文总结了使用CMake搭建编译环境的技巧,涵盖单个及多个源文件的编译、CMakeLists嵌套管理、变量设置、交叉编译配置、常用编译选项及警告处理等内容。通过实例说明了如何高效组织工程结构,并利用CMake灵活控制编译流程,适用于嵌入式开发场景。
1389 78
|
缓存 NoSQL Unix
【实战指南】守护进程服务实现
本文介绍了在Linux系统中实现守护进程异常重启的几种方案。通过理解僵死进程和信号处理机制,提出了基于SIGCHLD信号监听、轮询proc文件系统及waitpid接口的三种方法,并给出了C++实现代码。最终选择轮询方式以提升稳定性,确保服务进程在崩溃后能自动重启,保障系统可靠性。
544 96
|
监控 负载均衡 网络协议
TCP重传与超时机制:解锁网络性能之秘
TCP重传与超时机制:解锁网络性能之秘
4683 0
|
存储 安全 Linux
调整 core dump 的存储位置或限制
【10月更文挑战第1天】
1795 2
|
安全 Linux 网络安全
【工具使用】几款优秀的SSH连接客户端软件工具推荐FinalShell、Xshell、MobaXterm、OpenSSH、PUTTY、Terminus、mRemoteNG、Terminals等
【工具使用】几款优秀的SSH连接客户端软件工具推荐FinalShell、Xshell、MobaXterm、OpenSSH、PUTTY、Terminus、mRemoteNG、Terminals等
149347 0
|
Ubuntu 开发者 Python

热门文章

最新文章