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

本文涉及的产品
Serverless 应用引擎免费试用套餐包,4320000 CU,有效期3个月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
任务调度 XXL-JOB 版免费试用,400 元额度,开发版规格
简介: 本文记录了一次文件描述符(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文件来永久修改。

相关文章
|
2月前
|
缓存 NoSQL Unix
【实战指南】守护进程服务实现
本文介绍了在Linux系统中实现守护进程异常重启的几种方案。通过理解僵死进程和信号处理机制,提出了基于SIGCHLD信号监听、轮询proc文件系统及waitpid接口的三种方法,并给出了C++实现代码。最终选择轮询方式以提升稳定性,确保服务进程在崩溃后能自动重启,保障系统可靠性。
121 31
|
2月前
|
缓存 编译器 Shell
【实战指南】 CMake搭建编译环境总结
本文总结了使用CMake搭建编译环境的技巧,涵盖单个及多个源文件的编译、CMakeLists嵌套管理、变量设置、交叉编译配置、常用编译选项及警告处理等内容。通过实例说明了如何高效组织工程结构,并利用CMake灵活控制编译流程,适用于嵌入式开发场景。
357 21
|
11月前
|
人工智能 监控 供应链
AI技术创业有哪些机会?
本文探讨了AI技术创业的多个机会,包括提供行业解决方案、开发智能产品和服务以及教育和培训,为创业者在医疗保健、金融服务、零售、教育等多个领域提供了丰富的机遇。
462 2
|
2月前
|
设计模式 人工智能
单例模式中的隐藏陷阱:你真的了解单例吗?
本文通过一个实际案例揭示了单例模式中常见的隐藏陷阱——在析构函数中调用其他单例对象导致程序崩溃的问题。代码示例展示了因单例析构顺序不确定而引发的 `crash`,并结合 AI 的分析提出两种规避策略:一是避免析构、二是通过全局状态检测对象存活。文章指出,依赖不确定的语言特性并非良策,真正的解决之道在于合理设计,确保程序行为可预期、可控制。
151 44
|
2月前
|
C语言 C++
【实战指南】 C/C++ 枚举转字符串实现
本文介绍了在C/C++中实现枚举转字符串的实用技巧,通过宏定义与统一管理枚举名,提升代码调试效率并减少维护错误。
200 38
|
2月前
|
程序员 编译器 C++
【实战指南】C++ lambda表达式使用总结
Lambda表达式是C++11引入的特性,简洁灵活,可作为匿名函数使用,支持捕获变量,提升代码可读性与开发效率。本文详解其基本用法与捕获机制。
108 28
|
2月前
|
人工智能 自然语言处理 并行计算
Github 12k star ,Shap‑E 深度解析:秒生成 3D 模型,一文掌握应用与技巧
Shap-E 是 OpenAI 开源的创新工具,能将文本或图片秒级转换为高质量 3D 模型,支持 Mesh 与 NeRF 格式输出。具备快速生成、双模态输入、本地部署等优势,适用于游戏原型、3D 打印、内容创作等场景,GitHub 已获 12k+ 星标。
157 0
|
2月前
|
JSON API 数据安全/隐私保护
农业银行模拟器,工商建设邮政中国银行,虚拟余额生成模拟器
这个银行模拟系统包含三个主要模块:核心银行账户逻辑、图形用户界面和REST API接口
|
安全 编译器 C语言
深入分析宏定义的优势、局限性及最佳实践。
深入分析宏定义的优势、局限性及最佳实践。