使用 CreateToolhelp32Snapshot 获取句柄快照必须显式指定 TH32CS_SNAPHANDLE,TH32CS_SNAPALL 会忽略句柄枚举;该快照仅对当前进程有效,跨进程需先 OpenProcess 获取目标进程句柄,且需调试权限才能查询对象名。

Windows 上用 CreateToolhelp32Snapshot 拿句柄快照必须加 TH32CS_SNAPHANDLE
直接调 CreateToolhelp32Snapshot 却拿不到句柄?大概率是参数写错了。它不支持单独传 TH32CS_SNAPALL——这个标志会忽略句柄枚举,必须显式指定 TH32CS_SNAPHANDLE。而且注意:该快照只对当前进程有效,不能跨进程获取其他进程的句柄(除非你有调试权限并额外打开目标进程句柄)。
常见错误现象:Handle32First 返回 FALSE,GetLastError() 是 ERROR_INVALID_PARAMETER 或 ERROR_ACCESS_DENIED。
- 必须用
OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid)获取目标进程句柄(若要分析其他进程) - 快照句柄本身需用
CloseHandle释放,否则算作一次未关闭资源 - 返回的
HANDLEENTRY32结构里,htype字段是句柄类型编号(如 0x1e 是EVENT),不是字符串,查表需自己映射
Linux 下没有“句柄快照”概念,得靠 /proc/[pid]/fd/ 目录遍历
Linux 不提供类似 Windows 的统一句柄快照 API,所有打开的文件描述符都暴露在 /proc/self/fd/(当前进程)或 /proc/[pid]/fd/(其他进程)下。读取这个目录本质是读符号链接,每个项指向实际资源路径或设备节点。
使用场景:做轻量级泄露检测时,可定时扫描 fd 数量 + 类型分布(比如 socket:[12345]、anon_inode:[eventpoll]、pipe:[67890]),比 lsof 更快更可控。
- 不要用
opendir+readdir后再readlink—— 部分 fd 可能已在遍历中途关闭,readlink会失败,应忽略ENOENT -
/proc/[pid]/fd/下的数字名不连续,也不代表 fd 编号顺序,别假设 0/1/2 一定存在 - 某些 fd(如
inotify、eventfd)没有路径名,readlink返回空,需结合stat的st_mode判断类型
对比两次快照找泄露:别只数总数,要按类型+生命周期分组
单纯比较 “上次 1024 个 fd,这次 1032 个”,意义不大。真正的泄露往往藏在特定类型长期累积,比如每轮 HTTP 请求多开一个 epoll 实例却没 close,或反复 mmap 后忘了 munmap。
实操建议:把每次快照结果存成结构体数组,字段至少包含 fd(或 handle)、type、target_path(Linux)或 object_name(Windows,需用 NtQueryObject 补充)、采集时间戳。然后用 std::map 按类型聚合,再用 std::set 记录存活超过 N 轮的句柄。
- Windows 下想查句柄指向的对象名,必须调
NtQueryObject并传ObjectNameInformation,且需要SeDebugPrivilege权限,普通用户进程默认无权调用 - Linux 下
socket:[inode]的 inode 若持续增长且不复用,大概率是 socket 泄露;anon_inode:[eventpoll]增多则可能是 epoll 实例未释放 - 避免高频采样(如每毫秒一次),
/proc和 Toolhelp 都有开销,建议按业务周期采样(如每次请求前后、每 5 秒一次)
资源泄露监控上线前必须关掉调试器附加和 IDE 自动 attach
很多团队在本地验证通过,一上生产就误报“泄露”,根源是开发环境默认启用调试器附加(如 VS 的“仅我的代码”、CLion 的 GDB auto-attach)。这些工具会在目标进程内注入调试句柄、创建额外事件对象、甚至 hook 系统调用,导致快照中出现大量非业务句柄。
真实环境跑监控,第一件事就是确认进程启动时没被任何调试器控制:IsDebuggerPresent() 返回 FALSE(Windows),或检查 /proc/[pid]/status 中 TracerPid 是否为 0(Linux)。
- Windows 下即使没手动 attach,某些杀软或系统服务(如 Windows Defender Application Control)也会注入,需在干净虚拟机中 baseline
- Linux 下
strace -p [pid]会强制让TracerPid非零,监控程序需跳过这类进程 - 句柄计数本身不是泄露证据,只有“业务逻辑明确该释放却没释放”的资源,才值得告警
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











