服务器open‑files耗尽,应用进程ulimit
服务器 open-files 耗尽,通常会在应用日志里看到 Too many open files 或 open files: 1024 之类的报错。
这个问题的本质是 Linux 限制了单个进程能打开的文件描述符数量(包括文件、Socket 连接、管道等),一旦超过限制,应用就会拒绝新连接或读写失败。
本文会从现象判断、临时调整、永久配置和验证几个方面,带你把 ulimit nofile 调整到位。
先定位是哪个进程超过了限制
当你看到 open-files 耗尽相关报错时,不要急着改全局配置,先确认是哪个进程出了问题。
使用下面的命令查看进程 ID 及其当前文件句柄数:
# 按连接数或句柄数排序,找出可疑进程
ps -eo pid,comm,args | sort -k3 -n | tail -20
# 查看某个进程的当前句柄数和限制值
cat /proc/PID/limits | grep -i 'open files'
# 统计该进程实际打开的句柄数
ls -l /proc/PID/fd | wc -l
如果 soft limit 是 1024,而当前 fd 数量已经接近或超过 1024,基本可以确认是 ulimit nofile 设置过小。
这里的 soft limit 是应用实际可用的上限,hard limit 是最高可调整的边界。
临时调整:先让服务恢复可用
在确认问题后,可以先用 ulimit 命令临时提高当前 shell 的限制,但这只对当前会话生效,服务重启后会失效。
# 临时把当前 shell 的 nofile 提高到 65535
ulimit -n 65535
# 查看是否生效
ulimit -n
如果应用是通过 systemd 管理的,临时调整就不适合直接改 shell,因为 systemd 服务进程不会继承这个值。
对于 systemd 服务,可以用下面的方式临时生效:
systemctl edit 服务名
在打开的编辑器中写入:
[Service]
LimitNOFILE=65535
然后执行 systemctl restart 服务名 重启服务。
这种方式只针对单个服务,不会影响系统其他进程,比直接改全局配置更安全。
永久调整:修改 limits.conf 和 systemd 配置
要让配置在重启后依然生效,需要修改系统级或服务级的配置文件。
对于普通进程(不是 systemd 服务),编辑 /etc/security/limits.conf 文件,在末尾追加:
* soft nofile 65535
* hard nofile 65535
* 表示对所有用户生效,也可以换成具体的用户名。
修改后需要重新登录或重启服务才能加载。
对于 systemd 管理的服务,更推荐在服务单元中配置。
建议使用 systemctl edit 服务名 创建覆盖文件,而不是直接修改 /usr/lib/systemd/system/ 下的原文件,这样系统更新时不会被覆盖。
systemctl edit 服务名
写入:
[Service]
LimitNOFILE=infinity
infinity 表示不限制,实际生产环境建议按需设置,比如 65535 或 1048576,不要盲目使用无穷大,避免内存被异常进程拖垮。
验证调整是否生效的三种方法
调整后不能只看配置,必须确认进程实际加载了新的限制。
方法一:查看进程 limits 文件
cat /proc/PID/limits | grep 'open files'
替换 PID 为实际进程号,输出中的 soft limit 应该显示为你设置的值。
方法二:用 ulimit 命令查看
如果是当前 shell 直接启动的应用:
ulimit -n
方法三:观察错误是否消失
重启应用后,继续监控应用日志和 fd 数量:
# 持续观察句柄数是否还在上涨
while true; do ls -l /proc/PID/fd | wc -l; sleep 5; done
如果句柄数不再触及限制,并且没有新的 Too many open files 报错,说明调整生效。
避坑和常见问题
处理 open-files 耗尽时,有几个细节容易踩坑:
- 改完 limits.conf 后不生效:如果应用是 systemd 服务,limits.conf 默认不生效,必须使用
LimitNOFILE配置。同时确认是否超过了fs.file-max系统总限制,可用sysctl fs.file-max查看。 - hard limit 小于 soft limit:如果 hard limit 设置得比 soft limit 小,进程无法提升到更大的值。尽量保持 soft 和 hard 一致,或者 hard 大于等于 soft。
- 盲目设置 infinity:对于 Nginx、MySQL 这类可控进程,设置 65535 通常足够;对于 Java 应用,句柄消耗可能更剧烈,建议根据压测结果逐步上调,不要一次性拉满。
- 修改后必须重启进程:ulimit 限制在进程启动时加载,修改配置后不重启,旧进程继续使用旧限制。
最后补充一点
如果你正在处理服务器 open-files 耗尽、应用进程 ulimit nofile 调整的问题,建议先按本文步骤定位到具体进程,再决定用临时还是永久方案调整。
不要一上来就改全局配置,优先针对单个服务设置 LimitNOFILE,既不影响其他进程,也便于回滚。
验证时以 /proc/PID/limits 显示为准,不要只看配置文件里的数字。