直接查看 /proc/$pid/limits 是唯一可靠方式,它是内核维护的实时快照,精确反映进程当前资源限制;其他如 ulimit、limits.conf 或 systemctl show 均可能因作用域或生效时机不匹配而失真。

怎么看一个进程真正生效的资源限制
直接看 /proc/$pid/limits 是唯一可靠的方式。它不是配置文件,而是内核为该进程维护的实时快照,每行明确列出资源名、软限制、硬限制和单位。比如 Max open files 行告诉你这个 PID 此刻能打开多少文件描述符——不掺水,不继承错,也不受 shell 会话干扰。
常见错误:用 ps aux | grep myapp 找到 PID 后,直接写 cat /proc/$pid/limits 却看到空或 Permission denied。这是因为 $pid 是 shell 变量,必须先展开;正确写法是 cat /proc/1234/limits(把 1234 换成真实数字),或者用命令替换:cat /proc/$(pgrep -f "myapp")/limits。
- 普通用户只能查看自己启动的进程,其他用户的 PID 会报
Permission denied - 输出里某项显示
unlimited,不代表没上限——可能被 cgroup 或 systemd 的LimitNOFILE=截断了,得接着查systemctl show myapp.service | grep LimitNOFILE -
/proc/$pid/limits是只读的,echo ... > /proc/1234/limits必然失败,内核禁止写入
/etc/security/limits.conf 是配置起点,但不是生效终点
/etc/security/limits.conf 只在 PAM 登录会话建立时加载,对已运行的进程、systemd 服务、docker 容器、cron 任务完全无效。它只影响通过 login、su -、ssh 等方式启动的交互式 shell 及其子进程。
容易踩的坑:
-
*不匹配 root 用户,想管 root 得单独加一行root soft nofile 65536 - 配置写完不重启会话就无效,
ulimit -n还是旧值,必须新开终端或重新登录 - systemd 服务无视
limits.conf,必须在.service文件里显式写LimitNOFILE=65536,否则改了也是白改
系统级上限在哪查:别只盯着 ulimit
ulimit -n 再大,也跨不过内核天花板 /proc/sys/fs/file-max。这个值才是全系统能分配的文件描述符总数,所有进程加起来不能超它。
实操建议:
- 查当前系统总容量:
cat /proc/sys/fs/file-max - 查已分配/已使用量:
cat /proc/sys/fs/file-nr(三列:已分配、已释放、最大) - 临时调高:
echo 2097152 > /proc/sys/fs/file-max(需 root) - 永久生效:往
/etc/sysctl.conf加fs.file-max = 2097152,再跑sysctl -p
注意:file-max 和单个进程的 nofile 是两层限制,前者是池子大小,后者是每个进程能从池子里分多少——池子小,分再多也没用。
prlimit 是唯一能改运行中进程限制的工具
只有 prlimit 能在不重启进程的前提下动态修改 /proc/$pid/limits 里的值,但它权限卡得很死:
- 普通用户只能降低自己的软限制,或把软限制提至当前硬限制范围内
- 要提高硬限制,必须是 root
- 对 systemd 服务,
prlimit改完可能立刻被拉回——因为DefaultLimitNOFILE或 service 文件里的LimitNOFILE=会定期重置 - 子进程不会自动继承父进程被
prlimit修改后的限制,除非显式 fork+exec 重新加载环境
示例:给 PID 1234 提高文件描述符到 16384(软硬一致):prlimit -p 1234 --nofile=16384:16384。执行后务必再 cat /proc/1234/limits | grep "open files" 确认是否真生效。
真正麻烦的点不在怎么查,而在“查到的值为什么和预期不一致”——往往是因为限制来自多个源头(PAM、systemd、cgroup、内核参数),且它们之间有覆盖优先级。验证时必须一层层剥开,而不是只盯一个文件或命令。


















