Windows用NtQuerySystemInformation(SystemHandleInformationEx)查系统句柄总量,需提权;Linux读/proc/[pid]/fd目录统计文件描述符数;两者均无法获取单个句柄引用计数,应通过周期性采样监控趋势。

Windows上用NtQuerySystemInformation获取句柄总数
Windows内核不直接暴露每个句柄的引用计数,但能查系统级句柄总量——这是进程资源监控最实用的起点。关键函数是NtQuerySystemInformation,需加载ntdll.dll并声明对应结构体。
- 必须用
SystemHandleInformation(值为16)或更稳定的SystemHandleInformationEx(Win10 1809+,值为64),后者支持64位句柄数且无需自己计算结构体偏移 - 调用前要提升进程权限:
SeDebugPrivilege,否则返回STATUS_ACCESS_DENIED - 返回的缓冲区需手动解析:每条记录含
ProcessId、ObjectTypeNumber、HandleValue、HandleCount(注意:这不是引用计数,是该句柄在当前进程内的打开次数) - 示例片段:
auto status = NtQuerySystemInformation(SystemHandleInformationEx, buf, size, &retlen); if (status == STATUS_INFO_LENGTH_MISMATCH) { /* realloc and retry */ }
Linux上读取/proc/[pid]/fd目录统计打开文件数
Linux没有“句柄引用计数”概念,但/proc/[pid]/fd/目录下的符号链接数量就是该进程当前打开的文件描述符总数——这等价于Windows句柄总数用途,且无需特权。
- 对目标进程
pid执行scandir("/proc/1234/fd", ...)即可,过滤掉.和..后计数 - 注意:若进程已退出或
/proc不可见(如容器未挂载procfs),opendir会失败,需检查errno == ENOENT - 不要尝试
lstat每个fd项来判断是否有效——开销大且可能因竞态导致误判;只要目录存在且可读,链接数本身就有意义 - 对比
/proc/[pid]/status里的FDSize:字段只是内核预分配的fd数组大小,不是实际使用量
为什么不能拿到单个句柄的引用计数
无论是Windows还是Linux,用户态程序都**无法获取任意句柄/文件描述符的全局引用计数**。这不是API缺失,而是设计使然。
- Windows中句柄引用计数由对象管理器在内核维护,仅在
ObReferenceObjectByHandle等内部路径使用,未导出给用户态 - Linux中文件描述符指向
struct file,其f_count字段是原子计数,但仅在内核空间可见;/proc/[pid]/fdinfo/[fd]只显示flags和pos,不包含计数 - 试图通过反复
dup()或DuplicateHandle()再统计来反推?不可靠——其他线程或子进程可能同时操作同一资源,且无原子快照机制
监控建议:用周期性采样替代实时计数
真正稳定的进程资源监控,从来不是追求“某一毫秒的精确引用数”,而是观察趋势。高频采样+差值分析比单次全量扫描更实用也更轻量。
- Windows下每5秒调用一次
NtQuerySystemInformation,记录各进程句柄数变化,突增往往意味着泄漏(如没配CloseHandle的循环) - Linux下用
getdents64()代替scandir()可减少内存分配,尤其在fd数超万时 - 避免在信号处理函数里做这类操作——
NtQuerySystemInformation和scandir都不是async-signal-safe - 容器环境注意
/proc挂载选项:hidepid=2会导致非root进程看不到其他pid的fd目录,此时只能监控自身
句柄引用计数本身是个内核实现细节,暴露它既无必要也不安全。盯住总量变化、结合堆栈采样(如Windows ETW或Linux perf)定位泄漏点,才是实战中真正走得通的路。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











