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

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
云原生网关 MSE Higress,422元/月
任务调度 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文件来永久修改。

相关文章
|
人工智能 监控 供应链
AI技术创业有哪些机会?
本文探讨了AI技术创业的多个机会,包括提供行业解决方案、开发智能产品和服务以及教育和培训,为创业者在医疗保健、金融服务、零售、教育等多个领域提供了丰富的机遇。
609 2
|
4月前
|
缓存 NoSQL Unix
【实战指南】守护进程服务实现
本文介绍了在Linux系统中实现守护进程异常重启的几种方案。通过理解僵死进程和信号处理机制,提出了基于SIGCHLD信号监听、轮询proc文件系统及waitpid接口的三种方法,并给出了C++实现代码。最终选择轮询方式以提升稳定性,确保服务进程在崩溃后能自动重启,保障系统可靠性。
277 53
|
4月前
|
缓存 编译器 Shell
【实战指南】 CMake搭建编译环境总结
本文总结了使用CMake搭建编译环境的技巧,涵盖单个及多个源文件的编译、CMakeLists嵌套管理、变量设置、交叉编译配置、常用编译选项及警告处理等内容。通过实例说明了如何高效组织工程结构,并利用CMake灵活控制编译流程,适用于嵌入式开发场景。
646 34
|
6月前
|
人工智能 供应链 安全
MCP Server的五种主流架构与Nacos的选择
本文深入探讨了Model Context Protocol (MCP) 在企业级环境中的部署与管理挑战,详细解析了五种主流MCP架构模式(直连远程、代理连接远程、直连本地、本地代理连接本地、混合模式)的优缺点及适用场景,并结合Nacos服务治理框架,提供了实用的企业级MCP部署指南。通过Nacos MCP Router,实现MCP服务的统一管理和智能路由,助力金融、互联网、制造等行业根据数据安全、性能需求和扩展性要求选择合适架构。文章还展望了MCP在企业落地的关键方向,包括中心化注册、软件供应链控制和安全访问等完整解决方案。
3082 157
MCP Server的五种主流架构与Nacos的选择
|
Kubernetes 搜索推荐 前端开发
containerd 镜像构建工具 -- nerdctl 和 buildkit
containerd 镜像构建工具 -- nerdctl 和 buildkit
7901 0
|
4月前
|
设计模式 人工智能
单例模式中的隐藏陷阱:你真的了解单例吗?
本文通过一个实际案例揭示了单例模式中常见的隐藏陷阱——在析构函数中调用其他单例对象导致程序崩溃的问题。代码示例展示了因单例析构顺序不确定而引发的 `crash`,并结合 AI 的分析提出两种规避策略:一是避免析构、二是通过全局状态检测对象存活。文章指出,依赖不确定的语言特性并非良策,真正的解决之道在于合理设计,确保程序行为可预期、可控制。
220 56
|
3月前
|
运维 监控 测试技术
2025年微服务架构关键知识点(一):核心原则与演进趋势
微服务架构凭借其高可用性、灵活扩展等优势,已成为2025年主流软件开发范式。本文深入解析微服务的核心原则、演进趋势及实践要点,助力开发者夯实基础,应对挑战,构建高效、稳定的系统架构。
|
4月前
|
C语言 C++
【实战指南】 C/C++ 枚举转字符串实现
本文介绍了在C/C++中实现枚举转字符串的实用技巧,通过宏定义与统一管理枚举名,提升代码调试效率并减少维护错误。
337 53
|
4月前
|
程序员 编译器 C++
【实战指南】C++ lambda表达式使用总结
Lambda表达式是C++11引入的特性,简洁灵活,可作为匿名函数使用,支持捕获变量,提升代码可读性与开发效率。本文详解其基本用法与捕获机制。
177 47
|
4月前
|
设计模式 C++
【实战指南】设计模式 - 工厂模式
工厂模式是一种面向对象设计模式,通过定义“工厂”来创建具体产品实例。它包含简单工厂、工厂方法和抽象工厂三种形式,分别适用于不同复杂度的场景。简单工厂便于理解但扩展性差;工厂方法符合开闭原则,适合单一类型产品创建;抽象工厂支持多类型产品创建,但不便于新增产品种类。三者各有优缺点,适用于不同设计需求。
172 51

热门文章

最新文章