Linux 设计哲学中强调一切皆文件,文件也是重要的系统资源,不合理分配使用文件访问句柄同样可以引发系统故障,最常见的就是 Too Many Open Files。这里就从系统资源限制修改方式(limits.conf,ulimit,system.conf)、常见句柄超限排查命令(find,ulimit,lsof 等)、以及问题重现与应对手段来对整个脉络进行梳理,形成线上问题排查知识结构。
一、系统资源限制修改
1、limits.conf
limits.conf 是一种配置文件,用于描述命令 ulimit 可用的系统资源配置,包括文件句柄数。配置文件的完整路径是 /etc/security/limits.conf。
#This file sets the resource limits for the users logged in via PAM.
#It does not affect resource limits of the system services.
前两行注释说明了作用范围,对通过 PAM 登录的用户起作用,不会对系统服务(systemd)的资源限制起作用。补充一下,PAM(Pluggable Authentication Modules) 是 Linux 中常见的一种登录认证模块,包括通过 ssh 进行终端登录的用户。
最后举了几个示例用于说明如何修改资源限制。
#<domain> <type> <item> <value>
#
#* soft core 0
#* hard rss 10000
#@student hard nproc 20
#@faculty soft nproc 20
#@faculty hard nproc 50
#ftp hard nproc 0
#@student - maxlogins 4
注释对 item 类型有说明,比如 nproc 代表最大可使用进程数,nofile 代表最大可用文件句柄数。
修改资源限制需要重启操作系统(reboot)。
2、ulimit
ulimit 命令可以访问资源使用限制,常用命令是:
ulimit -a
查看软限制(Soft Limit)使用参数 S,查看硬限制(Hard Limit)使用参数 H。
软限制就是只提示告警不做强制要求的资源限制,硬限制就是超限就会拒绝服务的资源限制,只有 root 用户可以修改硬限制,其中软限制设置不能超过硬限制。
ulimit -a 得到的结果很直观,就是数值,unlimited 代表无限制。
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 3517
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 3517
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
这里关注 open flies,Linux 内核版本 3.10.0 默认的文件句柄软限制是 1024,硬限制是 4096。
当然,ulimit 命令仅适用于使用 shell 终端的会话过程。
3、system.conf
前面提到 limits.conf 修改不对 systemd 生效,systemd 资源限制的配置路径是 /etc/systemd/system.conf,修改对应的值是 DefaultLimitNPROC。比如修改成软限制 4096,硬限制 524288:
DefaultLimitNPROC=4096:524288
修改后需要重启操作系统生效。
最后,比对一下这几种修改资源限制的配置方式:
- 1、ulimit 只对当前 shell 会话过程有效,配置源来自 limits.conf
- 2、limits.conf 对所有非 systemd 进程有效,修改后需要重启操作系统生效
- 3、system.conf 对 systemd 有效,修改后需要重启操作系统生效
二、系统资源查找命令
1、查找组件日志
这种方式是直接从服务器日志中检索,比如 tomcat 中在网络连接中发生异常可以查找:
find . | xargs grep -ri "java.net.SocketException: Too many open files"
2、查看系统资源限制
ulimit -a
3、查看进程资源限制
检查修改是否生效,可以查看进程对应的资源设置,使用命令:
cat /proc/<PID>/limits
4、统计文件句柄使用次数
查找系统中访问文件最多的 15 个进程:
lsof | awk '{ print $1 " " $2; }' | sort -rn | uniq -c | sort -rn | head -15
比如,这里 PID 14986 的 JSVC 进程共使用文件句柄 47 万多次:
473995 Jsvc 14986
361086 Jsvc 14059
175381 Jsvc 14935
73255 Jsvc 13805
46305 Jsvc 14244
5870 XNIO-1 14465
4696 GC 14465
4660 XNIO-1 12012
4660 pool-3-th 12012
4560 XNIO-1 9735
4104 pool-3-th 9735
4104 G1 9735
3728 GC 12012
3708 http-nio- 9473
3648 GC 9735
根据业务量可以初步判断是否存在文件访问泄漏,从而评估是否需要增大系统或进程的文件句柄配额。
5、查看进程使用的文件句柄
lsof -p <pid>
6、查看文件是否被打开
查看当前路径下哪些文件被打开过,要是直接查看某个文件是否被打开过就直接跟文件名,要是带大写 D 表示包含子目录搜索,小写 d 表示不包含子目录仅在当前目录下搜索。
lsof +D ./
7、查看进程的网络连接
根据 PID 查找网络连接:
lsof -i -a -p 9473
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
java 9473 root 6u IPv6 147925 0t0 TCP *:17222 (LISTEN)
java 9473 root 242u IPv6 145874 0t0 TCP CentOS:38032->127.0.0.1:8356 (ESTABLISHED)
根据端口查找网络连接:
lsof -i :17222
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
java 9473 root 6u IPv6 147925 0t0 TCP *:17222 (LISTEN)
三、资源限制引发的问题
1、重现 Too many open files
测试一下 ulimit 效果,shell 终端连接服务器,直接将当前用户进程使用文件句柄上限改成 1:
ulimit -n 1
可以发现立即生效了,所有尝试访问文件的操作全都被拒绝了,比如打开文件(cat),甚至罗列目录下文件(ll)也会报错:
-bash: start_pipeline: pgrp pipe: Too many open files
cat: error while loading shared libraries: libc.so.6: cannot open shared object file: Error 24
当然 ulimit 只对当前会话有效,切换终端还是可以正常访问文件的。如果需要作用全局,可以修改 limits.conf 并重启操作系统。
2、文件句柄使用建议
由此可见,应用程序也需要考虑系统资源限制,否则就会从非程序本身导致程序问题,造成系统故障。
以文件句柄超限为例,当服务器访问量激增,超过系统分配文件句柄数上限,就会导致拒绝访问,比如读写数据库、建立网络连接、加载 jar 等都是文件访问操作,表现为运行可能正常但是拒绝提供服务,或者直接运行异常无法启动。
一种统一维护的配置方式是为每一个应用程序添加 systemd 作为守护进程,然后通过 system.conf 进行统一管理,包括文件句柄配额。