先查ulimit -n看软限制,再用cat /proc//limits | grep "max open files"确认进程限制,最后执行ls /proc//fd | wc -l统计实际使用量;若该数值接近或等于限制值,即可确认文件描述符耗尽。

怎么确认是不是文件描述符耗尽了
看到 Too many open files 错误,别急着改代码,先验证是不是真被耗尽。Linux 下每个进程有独立的 fd 限额,用 ulimit -n 查当前软限制;用 cat /proc/<pid>/limits | grep "Max open files"</pid> 看具体值(把 <pid></pid> 换成你的程序 PID)。更直接的是查当前用了多少:ls /proc/<pid>/fd | wc -l</pid> —— 这个数接近或等于 ulimit -n 输出,基本就坐实了。
哪些 C++ 代码最容易漏关句柄
C++ 本身不自动管理 fd,所有 open()、socket()、pipe()、epoll_create()、inotify_init() 返回的整数都是 fd,必须显式 close()。常见漏点:
- 异常路径没
close():比如open()成功后,后续某步抛异常,close()被跳过 - 条件分支遗漏:if/else 中只在某个分支 close,另一个分支忘了
- 循环中重复
open()但只在循环外 close 一次(或根本没 close) - 用
std::fstream却依赖析构关闭 —— 如果对象是全局或 static,析构时机不确定;若中途调用fd = fstream.rdbuf()->fd();又自己操作 fd,更易混乱
用 lsof 快速定位泄漏源头
运行中查 fd 类型比数数量更有价值。lsof -p <pid></pid> 列出所有打开项,重点关注:
- 重复出现大量
REG(普通文件)、CHR(设备)、PIPE或socket行,尤其是路径相似或无名的 - 同一文件被 open 多次却没 close,比如日志文件每轮都
open(O_APPEND)但不关 - socket 状态为
UNCONN或ESTABLISHED但连接早已失效,说明没做close()或没处理shutdown()后的清理
加 -n -P 参数可禁用 DNS 和端口名解析,提速;用 lsof -p <pid> | awk '{print }' | sort | uniq -c | sort -nr | head</pid> 快速统计高频路径。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
RAII 是唯一靠谱的防御手段
靠人写 close() 不现实,必须用 RAII 封装。标准库没提供通用 fd wrapper,得自己写或用 boost::interprocess::file_descriptor(慎用,它不 handle socket);更常用的是轻量封装:
struct FD {
int fd_ = -1;
FD(int fd) : fd_(fd) {}
~FD() { if (fd_ != -1) close(fd_); }
FD(const FD&) = delete;
FD& operator=(const FD&) = delete;
operator int() const { return fd_; }
};
// 使用:FD fd(open("log.txt", O_WRONLY|O_APPEND)); // 异常安全
注意:不要对 stdin/stdout/stderr(fd 0/1/2)用这种 wrapper,它们由系统管理;也不要封装 dup 出来的 fd,除非你明确负责它的生命周期。
真正难查的不是 open 没 close,而是 fork 后子进程继承了父进程所有 fd,而父进程以为自己关了就完事——结果子进程还在用。这类问题必须结合 /proc/<pid>/fd</pid> 和进程树一起看。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










