有一个关键细节特别容易被忽视:/etc/sysctl.conf、/etc/security/limits.conf 和 /etc/systemd/system.conf 这三个配置文件必须同时修改,缺一不可。并且完成修改后,还必须执行 sysctl -p 和 systemctl daemon-reload 让配置真正生效。只要漏掉其中任意一步,systemd 管理的服务进程句柄数限制依然会停留在默认值。

在 CentOS 7 中修改系统句柄数限制时,不能只调整 /etc/security/limits.conf,否则通过 systemd 启动的服务(例如 nginx、redis、ja va 应用)通常不会受到任何影响——这也是最常见、最容易踩坑的问题。
为什么 ulimit -n 和 limits.conf 对服务进程不生效
从 systemd v219 开始,systemd 会绕过 PAM 对 limits.conf 的常规加载逻辑,因此所有通过 systemctl start 启动的服务,默认继承的是 systemd 自身的资源限制,而不是用户登录 shell 中的 ulimit 值。
- 执行
cat /proc/$(pgrep -f "nginx|redis|ja va")/limits | grep "Max open files"时,实际看到的值往往仍然是4096或1024,即使你已经修改过limits.conf ulimit -n只对当前 shell 会话及其子进程生效,终端重开或重新登录后通常会恢复默认/etc/security/limits.conf只对 PAM 登录会话有效,例如通过 SSH 登录后执行的命令,但不会自动传递到 systemd 的 service context
必须同时修改三个地方才能真正生效
这三个位置都要配置,顺序虽然没有强制要求,但更建议按下面的步骤进行设置:
- 系统级总句柄上限:编辑
/etc/sysctl.conf,添加一行fs.file-max = 6553560,然后执行sysctl -p立即生效。该值应当 ≥ 单个进程最大句柄数 × 预估并发进程数量 - 用户级软硬限制:在
/etc/security/limits.conf文件末尾追加两行(*表示所有用户;如果只想针对指定用户生效,把*替换为具体用户名):* soft nofile 1048576* hard nofile 1048576 - systemd 全局默认限制:编辑
/etc/systemd/system.conf,取消注释并修改以下两项:DefaultLimitNOFILE=1048576DefaultLimitNPROC=1048576
修改完成后必须执行systemctl daemon-reload,否则即便重启服务,新配置也不会被 systemd 重新加载
注意 nr_open 内核上限这个隐藏限制
即使你把 hard nofile 设置为 2000000,如果内核参数 /proc/sys/fs/nr_open 小于这个值,root 用户可能会出现登录失败,或者服务启动时报错 Operation not permitted。
- 先查看当前内核上限:
cat /proc/sys/fs/nr_open(CentOS 7 默认通常为1048576) - 如果需要突破该限制,编辑
/etc/sysctl.conf,追加fs.nr_open = 2097152,然后执行sysctl -p - 这个值不建议设置得过高(例如超过 4M),否则可能导致内核内存分配失败,尤其是在内存较小的服务器上更容易出现问题
如何验证 CentOS 7 句柄数限制是否真正生效
不要只看配置文件已经写入就认为设置完成,必须进一步检查运行中的实际进程限制:
- 查看系统总上限:
cat /proc/sys/fs/file-max - 查看某个服务 PID 的实际句柄限制:
cat /proc/$(pidof nginx)/limits | grep "Max open files"(正常情况下应输出两列数值,并且都等于你设置的目标值) - 查看当前 shell 的限制:
ulimit -n(应能反映limits.conf中的配置) - 如果重启服务后仍然没有生效,检查是否使用过
systemctl edit xxx.service做了单服务覆盖配置,因为这种 per-service override 的优先级会高于全局设置
最后再提醒一次:修改 system.conf 或 sysctl.conf 之后,systemctl daemon-reload 和 sysctl -p 这两步绝对不能遗漏,否则前面的 CentOS 7 句柄数调整基本等于没做。另外,如果把 nr_open 调得很高,却没有同步提高 file-max,反而可能导致新进程无法正常 fork。
