先验证是否真达上限:执行ulimit -u查软限制,ps -o nlwp,pid,lwp,args -u $USER看用户各进程线程数,ps h -Led -o user | sort | uniq -c | sort -n统计各用户LWP总数;若用户nlwp总和接近ulimit -u且fork失败,则确为上限所致。

怎么确认当前用户进程数是否真打满了
别急着改配置,先验证是不是真到了上限。最直接的信号是 fork: Cannot allocate memory 或 su: cannot set user id: Resource temporarily unavailable,但这两者都可能是假象——真正瓶颈未必是 ulimit -u。
执行这三步快速定位:
-
ulimit -u查当前 shell 的软限制值(注意:只对新启动的进程生效) -
ps -o nlwp,pid,lwp,args -u $USER | sort -n看该用户所有进程及其线程数(nlwp列即轻量级进程数,也就是线程数) -
ps h -Led -o user | sort | uniq -c | sort -n统计所有用户的 LWP 总数,确认是否集中在某用户
如果某个用户的 nlwp 总和接近或等于 ulimit -u 输出值,且新进程 fork 失败,那基本就是它卡住了。
为什么改了 /etc/security/limits.conf 还不生效
因为普通用户根本不会读 /etc/security/limits.conf ——CentOS/RHEL 7+ 默认优先加载 /etc/security/limits.d/20-nproc.conf,而它里面通常写着 * soft nproc 4096,直接覆盖了 limits.conf 的设置。
检查并修正的关键点:
- 运行
cat /etc/security/limits.d/*nproc*找出实际生效的文件(常见名:20-nproc.conf、90-nproc.conf) - 编辑那个文件,把
* soft nproc和* hard nproc都设为需要的值,比如65535 - 注意:仅修改后必须新开一个 login shell(比如
su - $USER或重新 ssh 登录),ulimit -u才会更新 - 如果是 systemd 服务(如 nginx、java 服务),
limits.conf完全无效,得在.service文件里加LimitNPROC=65535
为什么 ulimit -u 设成 65535 但还是 fork 不了
因为 ulimit -u 只是用户级软硬限制,最终还受内核 PID 池总量压制。真实上限 = min(ulimit -u, /proc/sys/kernel/pid_max)。
查一下内核当前 PID 上限:
-
cat /proc/sys/kernel/pid_max—— 这是唯一可信值;sysctl kernel.pid_max可能缓存旧值 - x86_64 默认可能是
32768或4194304,但写入超过4194304会被静默截断,不报错也不生效 - 临时调高:
echo 65535 > /proc/sys/kernel/pid_max - 永久生效:在
/etc/sysctl.conf加kernel.pid_max = 65535,再运行sysctl -p
如果 /proc/sys/kernel/pid_max 是 32768,而你设了 ulimit -u 65535,那实际能用的还是 32768 —— 多余的限制毫无意义。
容器和 systemd 场景下最容易漏掉的配置点
Docker、Kubernetes、systemd 三者完全绕过 /etc/security/limits.d/,靠默认继承或完全忽略。
对应修复方式:
- Docker:启动容器时必须显式传参,
--ulimit nproc=65535:65535;docker run不加这个,ulimit -u就是宿主机 root 的默认值(常为unlimited或32768) - Kubernetes:在 Pod 的
securityContext下配limits.nproc: 65535,否则默认是1048576(但很多镜像 runtime 会二次限制) - systemd:全局改
/etc/systemd/system.conf的DefaultLimitNPROC=65535,或单个服务在.service文件里写LimitNPROC=65535;改完必须systemctl daemon-reload
排查时最容易忽略的是:你以为改了 limits.conf 就万事大吉,结果 Java 应用跑在 systemd 里,或者容器没透传 ulimit,ulimit -u 在容器里显示的仍是默认值 —— 这时候看日志里的 PID allocation failure 比看错误字符串更准。


















