总结下 fs.file-max,ulimit -n 和 lsof的异同
1 问题背景
最近排查一个大数据集群的性能问题,排查过程中发现通过命令 ulimit -n 获取的最大句柄数,和通过命令 cat /proc/$pid/limits 获取的最大句柄数不一致,特意总结了下相关知识,在此记录下。
2 操作系统全局级别的限制
LINUX在操作系统全局级别,通过参数 fs.file-max 控制了整个系统能够打开的文件的最大数,该参数是由kernel在内核层面限制的,适用于所有用户所有进程:
- 可以通过如下命令查看全局级别的限制:sysctl fs.file-max;
- 也可以通过如下命令查看全局级别的限制:cat /proc/sys/fs/file-max;
- 可以通过如下命令修改全局级别的限制:sysctl -w fs.file-max=xxx;
- 也可以查看或修改 fs.file-nr的值,该参数有三部分,第一部分是已经使用的文件句柄数,最后一部分是最大句柄数的限制;
3 用户级别的限制
可以在用户级别,配置某个特定用户,可以同时打开的最大文件句柄数,此时有以下几点需要注意:
- 该参数实质是控制某个特定用户所运行的所有进程,单一一个进程所能够同时打开的最大文件句柄数(每个用户的每个进程能够打开的最大文件数);
- 最大句柄数包括软限制和硬限制(soft limit and hard limit),且软限制小于等于硬限制,普通用户能够调整自己的 soft limit,而 hard limit只有root用户才能调整;
- 临时修改最大文件句柄数限制,可以使用命令 ulimit -n 65535:该命令对该会话下后续新启动的所有进程都会立即生效,但系统重启后修改会丢失(ulimit -Hn/ulimit -Sn);
- 永久修改文件句柄数,需要修改配置文件 /etc/security/limits.conf或/etc/security/limits.d/20-nproc.conf:该修改在系统重启后不会丢失,但用户需要重新登录才能使用这里的修改值;
- 更改参数前已经存在的进程,以及这些已经存在的进程 fork 出的新进程,其底层实际生效的 nproc,仍然是参数更改前的值;
- systemd 控制的 service unit, 是不受 /etc/security/limits.conf 和 /etc/security/limits.d/20-nproc.conf 中的配置参数影响的,对于他们需要在配置文件中配置 LimitNOFILE(推荐配置 /etc/systemd/system/.d/override.conf,而不是 /usr/lib/systemd/system/.service,因为通过rpm等包管理器升级时后者会被覆盖掉);
- 查看当前会话下用户级别的限制,可以使用命令 ulimit -a;
- The ulimit is for filehandles,it applies to files, directories, sockets, pipes epolls, eventfds, timerfds etc etc. The parameter is set at user level, but applied for each process;
- You need to relogin to use the changed parms in /etc/security/limits.conf;
- Old process's forked sub-process will inherit and use old value;
- A duplicted session from an old session may not be using the new ulimit settings, you need use ulimit -a to check;
- ulimit can also be set by environment variables in /etc/profile, etc.
- you can use prlimit to dynamically modify limit settings for a certain process;
- systemd service units will completely igrone ulimit settings.
4 特定进程当前生效的限制
通过上述描述可以发现,用户级别对最大句柄数的调整,对某个进程是否生效,还取决于该进程隶属于哪个会话,该进程的父子进程链如何,以及该进程是否受 systemd 管控的影响。
- 对于某个正在运行的进程,其实际生效的句柄数限制,可以通过如下命令查看:cat /proc/$pid/limits | grep "open files";
- 对于某个正在运行的进程,其已经打开的文件句柄数,可以通过如下命令行获得:ls /proc/$pid/fd |wc -l;
5 关于 lsof
最后,我们经常使用 lsof 查看某个进程或当前整个系统所打开的文件数,比如:
- 通过如下命令查看某个进程打开的所有文件:lsof -p $pid |grep / | awk '{print $9}'|sort | uniq;
- 通过如下命令查看当前系统打开的所有文件的句柄数:sudo lsof | wc -l;
- 需要注意,lsof 和 fs.file-nr统计的口径并不一致:lsof count is more than fs.file-nr count, and the reason is, file-nr ignores some of the directories which are considered as files by lsof;