windows用ntquerysysteminformation配合systemextendedhandleinformation统计当前进程句柄数,linux读取/proc/self/fd/目录项数,macos调用proc_pidinfo(proc_pidlistfd)获取fd数量;三者均需注意权限、路径及沙盒限制。

Windows 下用 NtQuerySystemInformation 查进程句柄数
标准 C++ 没有跨平台获取“当前进程打开句柄总数”的接口,Windows 上最直接的方式是调用 NT 内核函数 NtQuerySystemInformation,配合 SystemHandleInformation(或更安全的 SystemExtendedHandleInformation)。它能列出当前系统所有句柄,你需要遍历并统计属于当前进程 ID 的条目。
注意:NtQuerySystemInformation 是未公开 API,头文件不直接提供,需手动声明;且从 Windows 8.1 起推荐用 SystemExtendedHandleInformation,因为它包含进程 ID 字段,避免依赖句柄表索引推算进程归属(旧版 SystemHandleInformation 中进程 ID 存在偏移不确定性)。
实操建议:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 链接
ntdll.lib(或用GetProcAddress动态获取NtQuerySystemInformation地址) - 分配足够大的缓冲区(句柄数可能上千,初始尝试 64KB,失败时按返回所需大小重试)
- 用
GetCurrentProcessId()获取dwPID,与每个SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX的UniqueProcessId字段比对 - 不要假设结构体大小固定——
sizeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX)必须用实际定义,且需#pragma pack(1)对齐
Linux 下读取 /proc/self/fd/ 目录计数
Linux 没有“句柄总数”概念,但文件描述符(file descriptor)是等价抽象。当前进程的所有打开 fd 都映射在 /proc/self/fd/ 下,每个数字子目录对应一个 fd。直接统计该目录下非 . / .. 的条目即可,这是最轻量、最可靠的方法。
实操建议:
- 用
opendir("/proc/self/fd")+readdir()遍历,跳过"."和".." - 不需要
open()或stat()每个 fd——路径存在即表示有效打开 - 注意:某些 fd 可能被
close()后尚未被内核回收(极短暂),但/proc/self/fd/实时反映内核当前视图,结果可信 - POSIX 兼容,无需特权,glibc 2.2+ 均可运行
macOS 上用 proc_pidinfo 获取 fd 数量
macOS 不暴露类似 /proc 的虚拟文件系统,但提供了 libproc 接口。核心函数是 proc_pidinfo,传入 PROC_PIDLISTFD 类型,可获取当前进程所有 fd 的元信息数组,其返回值直接给出数量(第二个参数为 0 时仅返回所需缓冲区大小)。
实操建议:
- 需要
#include <libproc.h></libproc.h>并链接-lproc - 先调用一次
proc_pidinfo(getpid(), PROC_PIDLISTFD, 0, NULL, 0)获取所需字节数 - 再分配缓冲区,第二次调用获取完整
proc_fdinfo数组,返回值即为 fd 总数 - 该方法不依赖
/proc(macOS 默认不挂载),也不触发内核模块加载,稳定且高效
跨平台封装要注意的三个坑
如果写通用函数(比如 get_open_handle_count()),最容易翻车的地方不在逻辑,而在边界和隐式依赖:
- Windows 下若未启用
SeDebugPrivilege,NtQuerySystemInformation可能只返回本进程句柄(权限不足时静默截断),导致结果严重偏低——这不是 bug,是设计行为 - Linux 下
/proc/self/fd/是符号链接目录,readdir()返回的 d_name 是字符串,必须用isdigit()判断是否为纯数字,不能用strtol后比较——避免误判self、cwd等非数字项 - macOS 的
proc_pidinfo在沙盒进程(如 App Sandbox)中会被拒,返回-1且errno == EPERM,此时无法降级到其他方式,只能报错或返回 0
真正难的不是调哪个 API,而是判断“此刻我有没有权限、有没有路径、有没有对应接口可用”。别指望一次调用通吃三平台。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










