Linux ulimit 如何配置资源限制并排查常见问题?
Linux 里的 ulimit 用来限制一个 shell 以及它启动的子进程能使用多少系统资源。它最常见的用途,是防止某个程序打开太多文件、创建太多进程、占用过多栈空间,或者在崩溃时不生成 core 文件。
不过 ulimit 容易被误解:你在命令行里执行 ulimit -n 65535,只影响当前 shell 和它后面启动的进程;已经运行的服务不会自动改变;由 systemd 管理的服务,也不会因为你改了 /etc/security/limits.conf 就一定生效。
ulimit 到底限制什么?
ulimit 本质上是 shell 内置命令,用来查看或设置进程资源限制。最重要的两个概念是软限制和硬限制。
- 软限制(soft limit):当前实际生效的限制,普通用户通常可以调低,也可以在不超过硬限制的前提下调高。
- 硬限制(hard limit):软限制的上限,普通用户不能随便提高,通常需要 root 或具备相应权限的进程调整。
bashulimit -Sn # 查看 soft nofile ulimit -Hn # 查看 hard nofile ulimit -a # 查看全部限制
常用 ulimit 命令有哪些?
| 命令 | 含义 | 常见场景 |
|---|---|---|
ulimit -n | 最大打开文件数,等价于 nofile | Nginx、数据库、高并发连接 |
ulimit -u | 最大用户进程数,等价于 nproc | 防止进程或线程创建过多 |
ulimit -s | 栈大小,等价于 stack | 递归过深、线程栈配置 |
ulimit -c | core 文件大小,等价于 core | 程序崩溃排查 |
ulimit -l | 最大锁定内存,等价于 memlock | 数据库、DPDK、实时程序 |
ulimit -v | 最大虚拟内存 | 限制进程虚拟地址空间 |
ulimit -t | 最大 CPU 时间 | 限制 CPU 占用时间 |
ulimit -f | 最大文件大小 | 防止写出超大文件 |
临时把最大打开文件数调到 65535:
bashulimit -n 65535
启用 core 文件:
bashulimit -c unlimited
这类命令只对当前 shell 和它之后启动的子进程有效。
nofile、nproc、stack、core、memlock 分别怎么用?
nofile 控制一个进程最多能打开多少文件描述符。这里的“文件”不只是普通文件,还包括 socket、pipe、epoll fd 等。Web 服务、网关、数据库经常需要调大它。连接数一高,最先遇到的往往就是 Too many open files。
nproc 限制用户最多能拥有多少个进程。很多时候线程也会受到这个限制影响,所以 Java、Go、数据库或高并发程序创建线程失败时,也要检查它。
stack 控制进程线程栈大小。栈太小,递归深、局部变量大、线程模型复杂的程序可能崩溃;栈太大,又会让大量线程浪费地址空间。
core 控制程序崩溃时能否生成 core dump。core 文件还受系统 core_pattern、工作目录权限、磁盘空间、systemd-coredump 等因素影响,不能只看 ulimit -c。
memlock 控制进程最多能锁定多少内存,数据库、实时计算、DPDK、Elasticsearch 某些配置会关注它。
limits.conf 的格式怎么写?
永久配置通常写到 /etc/security/limits.conf 或 /etc/security/limits.d/*.conf。
格式是:
conf<domain> <type> <item> <value>
常见配置:
conf* soft nofile 65535 * hard nofile 65535 username soft nproc 4096 username hard nproc 8192 @groupname soft memlock 1048576 @groupname hard memlock 2097152 * soft core unlimited * hard core unlimited
domain 可以是 *、用户名、@组名;type 可以是 soft、hard、-;item 是 nofile、nproc、stack、core、memlock 等;value 是限制值或 unlimited。
生产环境里推荐把 soft、hard 分开写,便于排查到底哪个值没有生效。
为什么改了 limits.conf 还是没生效?
/etc/security/limits.conf 主要通过 PAM 的 pam_limits.so 在用户登录会话中生效。也就是说,它通常影响 SSH 登录、su、login 等 PAM 会话。
但 systemd 管理的服务不一定走这种登录链路。你改了 limits.conf,然后执行 systemctl restart nginx,Nginx 的限制值未必会变。systemd 服务应该用自己的配置方式。
systemd 服务如何设置 ulimit?
推荐用 drop-in 文件,不要直接改发行版自带的 service 文件。
bashsudo systemctl edit nginx
写入:
ini[Service] LimitNOFILE=65535 LimitNPROC=4096 LimitCORE=infinity LimitMEMLOCK=infinity
保存后执行:
bashsudo systemctl daemon-reload sudo systemctl restart nginx
再查看实际进程限制:
bashcat /proc/$(pidof nginx | awk '{print $1}')/limits
systemd 里常用 infinity 表示无限制,而 limits.conf 里常用 unlimited。
如何查看正在运行进程的真实限制?
不要只看当前终端的 ulimit -a。它只能说明当前 shell 的限制,不能代表某个服务进程。
bashcat /proc/PID/limits ls /proc/PID/fd | wc -l ps -eLf | grep PID | wc -l
如果要临时查看或调整某个已运行进程的限制,可以用 prlimit:
bashprlimit --pid PID prlimit --pid PID --nofile=65535:65535
prlimit 适合临时救急或验证问题,但生产配置还是应该落到 systemd drop-in、limits.d 或应用启动脚本里,避免重启后丢失。
fs.file-max 和 ulimit -n 是什么关系?
ulimit -n 控制的是单个进程的最大打开文件数。fs.file-max 控制的是整个系统层面的文件句柄上限。
bashcat /proc/sys/fs/file-max cat /proc/sys/fs/file-nr sysctl -w fs.file-max=2097152
如果单个进程的 nofile 很小,会先撞到 Too many open files;如果系统整体文件句柄耗尽,多个进程都可能异常。两者不是一个层面的限制,排查时要分开看。
常见故障怎么排查?
遇到 Too many open files,先确认是哪一层不够:
bashcat /proc/PID/limits | grep "open files" ls /proc/PID/fd | wc -l cat /proc/sys/fs/file-nr
如果进程 fd 数接近 Max open files,调大 nofile。如果是 systemd 服务,要配置 LimitNOFILE=,而不是只改当前 shell 的 ulimit -n。
core 文件没有生成时,检查 ulimit -c、/proc/PID/limits、/proc/sys/kernel/core_pattern、目录权限和磁盘空间。
进程或线程创建失败时,重点看 nproc。内存相关报错还要一起检查系统内存、cgroup 限制、容器限制、swap、overcommit 策略。
配置 ulimit 的最佳实践
先看真实进程限制,优先用 /proc/PID/limits。区分临时和永久:命令行 ulimit 适合临时验证,长期配置要落到配置文件。区分登录用户和服务:PAM 登录会话看 limits.conf / limits.d,systemd 服务看 LimitNOFILE=、LimitNPROC= 等 drop-in 配置。
不要盲目写 unlimited。core、memlock、nofile 都可能影响系统稳定性或磁盘空间。配置完必须重启服务并用 /proc/PID/limits 确认,而不是相信配置文件已经生效。
简单记住一句话:ulimit 管的是进程资源上限;limits.conf 多数影响 PAM 登录会话;systemd 服务要用 Limit* 配置;排查时以 /proc/PID/limits 看到的值为准。