必须显式传TH32CS_SNAPHANDLE才能获取句柄快照,TH32CS_SNAPALL不包含句柄信息且已弃用;遍历句柄需检查GetLastError()而非返回值,对ERROR_ACCESS_DENIED可补查NtQueryObject;唯一键应由dwHandle、dwType和lpObjectName哈希生成,避免仅比数量。

Windows 上用 CreateToolhelp32Snapshot 拿句柄快照必须加 TH32CS_SNAPHANDLE
直接调 CreateToolhelp32Snapshot 却拿不到句柄?大概率是参数漏了。它不默认包含句柄信息,必须显式传 TH32CS_SNAPHANDLE —— 其他标志如 TH32CS_SNAPPROCESS 或 TH32CS_SNAPTHREAD 对句柄列表完全无效。
常见错误是只传 TH32CS_SNAPALL,但这个宏在旧版 Windows SDK 中**不包含**句柄快照能力(MSDN 明确标注已弃用且行为未定义),实际运行可能返回空或崩溃。
-
CreateToolhelp32Snapshot(TH32CS_SNAPHANDLE, 0)是唯一可靠起点 - 快照句柄必须配对
CloseHandle(),否则自身就成泄露源 - 目标进程 ID 需有
PROCESS_DUP_HANDLE权限,普通进程需以管理员权限启动监控程序
遍历句柄时 GetHandleInformation 返回 0 不代表失败,得查 GetLastError()
很多代码看到 GetHandleInformation 返回 0 就跳过,结果漏掉大量有效句柄。它返回 0 只表示“信息不可获取”,比如内核对象未导出类型、或句柄已被关闭但尚未从表中清理——这恰恰是泄露线索。
真正判断是否该继续分析,要看 GetLastError():若为 ERROR_INVALID_HANDLE,说明句柄已失效;若为 ERROR_ACCESS_DENIED,说明权限不足但句柄仍存在;只有 ERROR_SUCCESS 才能安全调 GetFinalPathNameByHandle 查文件路径。
- 别用返回值真假做流程分支,必须检查
GetLastError() - 对
ERROR_ACCESS_DENIED的句柄,可尝试用NtQueryObject(需ntdll.lib)补查对象类型 - 频繁调用
GetFinalPathNameByHandle会显著拖慢快照速度,建议仅对疑似文件/事件/互斥体句柄触发
对比两次快照找泄露,不能只比数量,要哈希 dwHandle + dwType + lpObjectName
单纯统计句柄数增减毫无意义:线程创建/销毁、临时文件打开/关闭都会导致波动。真实泄露是“同类型、同名称、同生命周期的句柄持续累积”。
实操中应为每个句柄生成唯一键:std::hash。注意 lpObjectName 并非所有对象都有(如匿名管道句柄为空),此时用 dwHandle 和 dwType 组合足够区分。
- 避免用
std::map存句柄——HANDLE在不同快照间不具可比性,重用值很常见 - 两次快照间隔不宜短于 100ms,否则高频系统线程句柄抖动干扰判断
- 记录新增句柄时,同步抓取调用栈(需
StackWalk64+ PDB),否则定位不到泄露点
Linux 下没等价 API,/proc/[pid]/fd/ 只能看文件描述符,无法覆盖所有资源类型
别试图把 Windows 套路平移过去。/proc/[pid]/fd/ 列出的是 fd,对应内核 file 结构,但像 pthread mutex、mmap 区域、eventfd、timerfd 这些都不在此列。真正想监控全量资源,得走 eBPF:用 bpf_probe_attach 挂在 sys_open/sys_socket/sys_eventfd 等入口,再聚合计数。
用户态折中方案是组合使用:/proc/[pid]/status(看 FDSize、Threads)、/proc/[pid]/maps(查 mmap 泄露)、/proc/[pid]/stack(查卡死线程)。但这些全无实时性,且需 root 权限读其他进程。
- Linux 没有跨进程句柄概念,“资源泄露监控”本质是“系统级对象生命周期跟踪”,必须换思路
- glibc 的
mallinfo或malloc_stats只管堆,和句柄/描述符无关 - 如果项目要跨平台,Windows 用 Toolhelp + NtQueryObject,Linux 必须切到 eBPF 或至少
libbpf,别硬写统一接口
句柄泄露最常藏在异常路径里:析构函数没执行、回调里忘了 CloseHandle、RAII 对象被 move 后原实例仍持有句柄。监控本身只能报警,根因还得靠带符号的栈回溯 —— 而那部分,永远比快照逻辑难调。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











