最可靠方式是调用ntquerysysteminformation并传入systemhandleinformationex(win10 1809+),需动态加载ntdll.dll、处理status_info_length_mismatch重试、遍历匹配getcurrentprocessid(),无需sedebugprivilege查自身进程。

Windows下用NtQuerySystemInformation获取句柄总数
Windows没有公开的API直接返回当前进程的句柄数,NtQuerySystemInformation 是最可靠的方式,但要注意它属于未文档化的NT API,需手动加载。系统会返回所有进程的句柄信息,得自己遍历匹配当前GetCurrentProcessId()。
常见错误是直接调用失败——因为函数在ntdll.dll里,不能通过头文件链接,必须GetProcAddress动态获取;另外缓冲区大小容易预估不足,第一次调用通常会返回STATUS_INFO_LENGTH_MISMATCH,需重试。
- 先调用一次获取所需缓冲区大小,再分配内存重试
- 结构体
SYSTEM_HANDLE_INFORMATION_EX(Win10+)比旧版SYSTEM_HANDLE_INFORMATION更稳定,含句柄类型和对象名长度字段 - 遍历时注意:每个句柄条目大小不固定,要按
NumberOfHandles和HandleCount字段步进,别用sizeof硬算偏移 - 权限问题:需要启用
SeDebugPrivilege才能查到其他进程句柄,但查自己进程通常不需要
Linux下读取/proc/self/fd目录统计句柄数
Linux简单得多:/proc/self/fd 是个符号链接目录,里面每个文件代表一个打开的文件描述符(包括socket、pipe、eventfd等),readdir或scandir遍历即可得到总数。
注意这不是“资源占用”的全貌——它只反映fd数量,不包含内存映射区域(mmap)、线程栈、信号处理等开销。真正评估资源压力得结合/proc/self/status里的VmRSS、Threads、FDSize等字段。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
-
opendir("/proc/self/fd")失败可能是procfs没挂载,或容器环境被限制(如Docker默认禁用procfs部分挂载) - 某些fd是
[timerfd]、[eventpoll]这类内核对象,readlink会返回空字符串,不影响计数 - 不要用
ls -l /proc/self/fd | wc -l这种shell方式——在高并发场景下可能因fork/exec引入竞态,C++里应直接系统调用
跨平台统计不现实,优先按目标系统选方案
没有POSIX或C++标准库函数能统一获取句柄/文件描述符总数。试图封装一层抽象只会掩盖差异,比如Windows句柄包含GUI对象(窗口、GDI画笔),而Linux fd不包含这些;macOS用proc_pidinfo查PROC_PIDLISTFDS,接口又不同。
如果你写的是服务程序,真正该关注的不是“总数”,而是泄漏迹象:比如句柄数随请求单调增长、接近ulimit -n上限、或open() / CreateFileW开始返回EMFILE / ERROR_TOO_MANY_OPEN_FILES。
- Windows开发建议加
#ifdef _WIN32分支,直接调NtQuerySystemInformation - Linux建议同时读
/proc/self/fd和/proc/self/status,把FDSize(分配的fd槽位数)和实际使用数一起记录 - 别依赖
getrlimit(RLIMIT_NOFILE, &rlim)——它只返回上限,不是当前用量
调试时别只看总数,重点检查异常增长模式
句柄泄漏往往表现为每处理一个请求就多几个句柄,但重启后归零。这时候光看总数没用,得抓快照对比:lsof -p $(pidof yourapp) 或 Windows 上用handle.exe -p yourapp.exe导出明细,再用diff看新增了哪些类型。
常见坑是忘记关闭std::fstream、boost::asio::ip::tcp::socket析构失败、或dup2后没关原fd。C++ RAII能缓解,但第三方库回调里开的fd(比如libcurl的DNS socket)未必受控。
- Windows上
handle.exe输出里Event、Section类句柄暴涨,大概率是WaitForMultipleObjects没配好超时或没清理 - Linux上
socket:[…]数量突增,检查是否忘了close()或shutdown()后没关 - 日志里打句柄数时,别用
std::cout——它本身会占用fd,极端情况下可能把自己卡死
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










