Linux 下用 /proc/self/fd 目录统计最可靠,通过 opendir() 遍历目录项计数(跳过 . 和 ..),避免 shell 调用和 getrlimit;macOS 用 sysctl(KERN_PROC_FILEDESC),Windows 用 NtQuerySystemInformation(SystemHandleInformation) 过滤自身句柄。

Linux 下用 /proc/self/fd 目录统计最可靠
Linux 内核会为每个进程在 /proc/self/fd 下创建符号链接,每个链接对应一个打开的文件描述符。只要进程有读取 /proc 的权限(通常都有),就能直接统计数量。
实操建议:
- 用
opendir()打开/proc/self/fd,遍历目录项,跳过.和..,其余条目数即为 fd 总数(注意:部分条目可能是失效链接,但readdir()仍会计入) - 避免用
system("ls -1 /proc/self/fd | wc -l")——启动 shell 开销大,且可能被沙箱或容器限制 - 不要依赖
getrlimit(RLIMIT_NOFILE, &rlim)返回的rlim.rlim_cur,那是上限值,不是当前使用量
macOS 和 FreeBSD 需用 sysctl + KERN_PROC_FILEDESC
macOS 不提供 /proc,需调用 sysctl() 获取内核提供的进程文件描述符信息。接口不稳定、字段偏底层,容易因结构体版本变化出错。
实操建议:
- 先调用一次
sysctl()带NULL缓冲区,获取所需缓冲区大小;再分配内存重试 - 检查返回的
kinfo_file数组长度(struct kinfo_file *),而非硬编码偏移 - 注意:该接口在 macOS 12+ 上对非 root 进程可能只返回部分 fd(尤其 socket 或受 sandbox 保护的句柄),结果偏小是正常现象
Windows 上必须用 NtQuerySystemInformation + SystemHandleInformation
Windows 没有“文件描述符”概念,只有句柄(handle),且进程无法直接枚举自身句柄——必须用未公开的 NT API 查询全系统句柄表,再按 ProcessId 过滤。
实操建议:
- 加载
ntdll.dll,获取NtQuerySystemInformation地址,传入SystemHandleInformation(值为 16) - 循环重试:首次调用常因缓冲区不足失败,需根据返回的
ReturnLength重新分配内存 - 遍历返回的
SYSTEM_HANDLE_INFORMATION数组时,严格比对ProcessId == GetCurrentProcessId(),并忽略ObjectTypeNumber == 0(无效项) - 别用
GetStdHandle()或_get_osfhandle(),它们只覆盖标准流和 CRT 映射句柄,漏掉CreateFile、socket()等原生句柄
跨平台封装要注意三类边界问题
统一接口看似简单,但实际集成时最容易栽在隐式假设上:比如认为“所有句柄都是非负整数”(Windows handle 是指针值)、或“fd 从 0 开始连续”(Linux 可能存在空洞)、或“统计瞬间值绝对准确”(fd 在遍历过程中可能被 close/open)。
关键提醒:
- Linux 下
/proc/self/fd统计快但非原子——若业务逻辑对精度要求极高(如调试器或资源审计),应配合fcntl(fd, F_GETFD)二次验证链接有效性 - macOS 的
sysctl调用可能被 SIP(System Integrity Protection)静默截断,普通用户进程无法看到某些系统守护进程打开的 fd,但自己的 fd 一般完整 - Windows 下该方法需要
SeDebugPrivilege权限才能成功查询其他进程,但查自己进程不需要——不过仍建议以普通权限运行,避免提权副作用
真正难的不是调哪个 API,而是接受“没有 100% 准确、100% 安全、100% 快速的跨平台方案”。选哪个路径,取决于你更容忍精度偏差、权限限制,还是实现复杂度。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











