最可靠方法是遍历 /proc/[pid]/fd/ 并按 /proc/[pid]/status 中的 uid 过滤:先用 ps -eo pid,user 获取目标用户所有 pid,再对每个 pid 执行 ls -1 /proc/$pid/fd/ 2>/dev/null | wc -l 累加,避免 lsof -u 的截断、权限错误和已分配未释放 fd 漏计问题。

怎么查某个用户所有进程的句柄总数
不能只看一个进程,得汇总该用户下所有进程当前实际打开的 fd 数量。最可靠的方式是遍历 /proc/[pid]/fd/ 并按 /proc/[pid]/status 中的 Uid 字段过滤——因为 lsof -u username 会漏数、卡顿,尤其在 fd 过万时容易截断或权限报错。
实操建议:
- 先获取目标用户名(比如
www-data),再用ps -eo pid,user | awk '$2=="www-data"{print $1}'拿到所有 PID - 对每个 PID 执行
ls -1 /proc/$pid/fd/ 2>/dev/null | wc -l,并累加结果(注意必须加2>/dev/null,否则权限不足会混入错误行导致计数偏高) - 写成一行脚本更稳:
ps -eo pid,user | awk '$2=="www-data"{print $1}' | xargs -r -I{} sh -c 'ls -1 /proc/{}/fd/ 2>/dev/null | wc -l' | awk '{s+=$1} END{print s+0}' - 普通用户只能查自己进程;root 可查全部,但需注意某些容器或 sandbox 进程的
/proc/[pid]/fd/可能不可见(如 PID 命名空间隔离)
为什么 lsof -u 不准
lsof -u username 看起来方便,但实际生产环境常出偏差:它依赖内核符号表扫描 + 用户态格式化,当目标用户有上百个进程、每个进程打开数千 fd 时,lsof 自身会因内存分配失败或超时而提前退出,输出行数明显少于真实值;另外,它无法区分已 close() 但尚未被内核回收的 fd(即 file-nr 第二列覆盖的“已分配未释放”状态)。
对比验证方法:
- 用上面脚本算出用户总 fd 数 A
- 执行
lsof -u username | wc -l得到 B - 若
A - B > 100,基本可判定lsof丢数;差值越大,说明该用户存在大量短生命周期 fd 或高并发连接
如何确认该用户的软硬限制是否够用
用户级限制不等于 shell 当前 ulimit -n,而是其启动的所有进程继承的 RLIMIT_NOFILE,这个值记录在每个进程的 /proc/[pid]/limits 里。不能只查一个进程,要抽样检查多个关键进程(如 nginx worker、java 应用主进程)。
Linux AI 服务器 — 将 Linux 服务器转换为本地 AI 推理集群。使用 systemd、NVIDIA CUDA 实现无头 Linux AI,零 GUI 开销。
实操建议:
- 挑 3–5 个该用户的活跃进程 PID,执行
grep "Max open files" /proc/[pid]/limits - 如果输出中
Soft Limit是 1024,而你刚算出该用户已用掉 8000+ fd,说明限制早已打穿,哪怕系统file-max还剩一半也没用 - 注意:systemd 服务可能绕过
/etc/security/limits.conf,改用LimitNOFILE=配置在 service 文件里,此时得查systemctl show [service] | grep LimitNOFILE
排查该用户句柄增长是否异常
单纯知道“现在用了多少”不够,得看“是不是在涨”。重点不是定时跑一次命令,而是绑定进程生命周期观察趋势——比如某 Java 服务以用户 appuser 启动,上线后 2 小时内 fd 从 200 涨到 6000,大概率是连接未关闭或日志轮转泄漏。
快速定位手段:
- 用
watch -n 30 'ps -eo pid,user | awk '\''\$2=="appuser"{print \$1}'\'' | xargs -r -I{} sh -c '\''ls -1 /proc/{}/fd/ 2>/dev/null | wc -l'\'' | awk '\''{s+=\$1} END{print s+0}'\'''每 30 秒刷新一次总数 - 同时抓一个时间点的详细分布:
ps -eo pid,user | awk '$2=="appuser"{print $1}' | xargs -r -I{} sh -c 'echo "PID:{}"; ls -l /proc/{}/fd/ 2>/dev/null | head -n 20',看是否大量socket:[...]或重复路径的文件句柄 - 如果发现某类 fd(如
anon_inode:epoll)占比突增,可能是事件循环未正确释放资源,而非文件泄漏
真正难的不是算总数,而是判断“哪些 fd 本不该存在”。比如一个 cron 脚本每次执行都 fork 出新进程却没 wait,残留的子进程 fd 会持续累积;又或者 Python 的 subprocess.Popen 忘了设 close_fds=True,导致父进程把所有 fd 都传给了子进程。这些细节不会体现在总数里,但会让 /proc/[pid]/fd/ 下突然多出几百个编号跳跃的链接。










