linux文件最大打开数需协同配置三层限制:单进程软硬限制(ulimit -sn/-hn)、用户级limits.conf、系统级fs.file-max与nr_open;任一层缺失或冲突均导致“too many open files”错误,尤其需注意nr_open上限及systemd服务绕过pam limits的特性。

Linux 文件最大打开数不是单个配置能搞定的,它分三层:单进程软硬限制、用户级默认限制、系统全局上限。改错一层,服务照样报 Too many open files。
ulimit -Sn 和 ulimit -Hn 看到的值为什么改不动?
软限制(ulimit -Sn)不能超过硬限制(ulimit -Hn),而硬限制又受限于 /proc/sys/fs/nr_open —— 这是内核给单个进程划的“天花板”。很多用户直接在 limits.conf 里写 * hard nofile 1000000,结果登录后 ulimit -Hn 仍是 65536,就是因为 nr_open 默认是 1048576,但某些发行版(如较老 CentOS)实际生效值更低,或被 systemd 覆盖。
实操建议:
- 先查真实上限:
cat /proc/sys/fs/nr_open - 若需设 hard limit > 当前
nr_open值,必须先改内核参数:echo 'fs.nr_open = 2000000' | sudo tee -a /etc/sysctl.conf && sudo sysctl -p -
limits.conf中的 hard 值不能超过这个新nr_open,否则静默失效 - 非 root 用户无法提升自己的 hard limit,只能由 root 配置或通过 PAM 加载
/etc/security/limits.conf 修改后不生效的常见原因
最常踩的坑不是配置写错,而是 PAM 没启用或服务没走 PAM 登录流程。比如用 systemd 启动的服务(Nginx、Redis、Java 应用),默认绕过 limits.conf,除非显式声明。
实操建议:
- 确认
/etc/pam.d/common-session或对应服务的 PAM 配置里有这行:session required pam_limits.so - 对 systemd 服务,不能只靠
limits.conf,必须在 service 文件中加:LimitNOFILE=65535 - 改完
limits.conf后,必须退出当前 shell 并**新建登录会话**(不是su切换),否则旧 session 的 ulimit 不变 - 如果用 SSH 连入,确保不是用
ssh -o RequestTTY=no这类无 TTY 方式,它可能跳过 PAM limits 模块
fs.file-max 设置多大才合理?
fs.file-max 是整个内核能分配的文件句柄总数,不是每个进程的限额。它的值要大于「并发连接数 × 平均每连接占用句柄数」再留 20% 余量。比如跑 1000 个 Nginx worker,每个 worker 平均开 200 个连接,那至少要 1000 × 200 × 1.2 = 240000。
实操建议:
- 查看当前使用情况:
cat /proc/sys/fs/file-nr,输出三列分别是「已分配句柄数」「已使用句柄数」「最大可分配数(即fs.file-max)」 - 临时调高:
sudo sysctl -w fs.file-max=1048576 - 永久生效:写入
/etc/sysctl.conf后必须运行sudo sysctl -p,否则重启前也不生效 - 不要盲目设成极大值(如 10^9),会浪费内存且可能触发内核警告;Oracle 官方建议下限是 65536,生产环境通常设为 524288~2097152 区间
systemd 服务绕过 limits.conf 怎么办?
Ubuntu 16.04+、CentOS 7+ 默认用 systemd 管理服务,其启动过程不读 limits.conf。即使你给 www-data 配了 65535,Nginx 主进程启动时看到的仍是默认 4096。
实操建议:
- 编辑服务 unit 文件:
sudo systemctl edit nginx.service - 写入:
[Service] LimitNOFILE=65535
- 重载并重启:
sudo systemctl daemon-reload && sudo systemctl restart nginx - 验证是否生效:
cat /proc/$(pgrep nginx | head -n1)/limits | grep "Max open files" - 注意:如果服务由 root 启动但 drop privileges 到普通用户(如 Nginx),
LimitNOFILE必须设在主进程 unit 中,子进程继承该限制
真正容易被忽略的是 nr_open 和 systemd 的双重约束——前者决定单进程上限能否突破,后者决定服务到底认不认你的 limits.conf。这两处不处理,其他所有配置都是纸面功夫。











