概述
在高负荷的 Linux 服务器或者代码出现了 Bug 的环境中经常出现 “too many open files”(以下简写为 TMOF) 的错误,这意味着一个进程已经打开太多的文件(文件描述符),无法再打开新的文件了。在 Linux 中,每个进程或用户的最大打开文件限制是有默认设置的,而且数值相当小。
在这篇文章中,我将介绍如何在 Linux 中检查当前最大打开文件的限制,以及如何为整个主机、单个服务或当前会话改变它。
TMOF 错误以及打开文件的限制
首先,让我们看看 “too many open files” 的错误出现在哪里。最常见的是在安装了 web 服务(NGINX/httpd )或数据库服务(MySQL/MariaDB/PostgreSQL)的机器上,当读取大量的日志时,就会出现这种情况。例如,当 Nginx 网络服务器超过 最大可打开文件 的限制时,你会看到一个错误:
socket () failed (29: Too many open files) while connecting to upstream
图 1:Nginx 出现 too many open files |
---|
使用这个命令,你可以得到系统可以打开的最大文件描述符的数量:
[root@liqiang.io]# cat /proc/sys/fs/file-max
22854988
当前用户的打开文件限制是 1024,你可以通过这条命令检查:
[root@liqiang.io]# ulimit -n
1024
其实这里有两种限制类型:硬限制和软限制,用户可以改变软限制(但是,软限制值不能超过硬限制),只有特权用户或根用户可以修改硬限制值。要显示软限制,可以运行以下命令:
[root@liqiang.io]# ulimit -Sn
65535
相应地,你可以通过这个命令查看硬限制:
[root@liqiang.io]# ulimit -Hn
65535
增加系统最大文件限制数
可以通过改变 Linux 操作系统中的限制来允许所有的服务打开大量的文件。为了让新的设置持久化,防止它们在服务器或会话重启后被重置,必须对 /etc/security/limits.conf
做出修改,具体为在这个文件中添加这些行:
[root@liqiang.io]# tail -2 /etc/security/limits.conf
root soft nofile 65535
root hard nofile 65535
增加单个服务的最大文件限制数
毕竟增加整个系统的文件描述符数量不是一个很好的事情,如果可以针对服务来控制的话,就更好了,事实上也是可以做的,这里我是使用 systemd 来做的:
[root@liqiang.io]# grep -A 2 Service /usr/lib/systemd/system/nginx.service
[Service]
LimitNOFILE=1024
LimitNOFILESoft=1024
[root@liqiang.io]# systemctl daemon-reload
[root@liqiang.io]# systemctl restart nginx
验证一下文件的最大打开文件数:
[root@liqiang.io]# cat /proc/230490/limits | grep "Max open files"
Max open files 1024 4096 files
修改当前会话的最大文件限制数
修改当前会话的文件描述符限制数可以很简单地通过 ulimit 命令做到:
[root@liqiang.io]# ulimit -n 2048
但是,很明显,在重新登陆之后,这个配置就会失效了,被恢复成 /etc/security/limits.conf
中的值,如果也想持久化一把,还可以这么改:
[root@liqiang.io]# grep "fs.file-max" /etc/sysctl.conf
fs.file-max = 4096
[root@liqiang.io]# sysctl -p
归纳总结
这三个层级的先后顺序是:
- 当系统启动的时候,先读取系统级别的句柄数限制(/proc/sys/fs/file-max)
- 如果用户设置了(/etc/security/limits.conf),那么就会再次加载这份配置
- 可以大于 file-max(这个只是建议值)
- 如果用户设置了会话级别的,那么就使用会话级别覆盖
- 如果用户设置了服务级别的,那么同样使用服务级别的设置覆盖
一些问题
Soft 和 Hard 的区别是啥
没有本质区别,但是有些许的权限区别:
- 只有 root 用户可以提高 hard
- 非 root 用户和 root 运行的进程可以降低 hard
- 所有用户都可以修改 soft,但是,不能超过 hard
- 没有强制限制,但是即使超过了 hard,实际使用的时候也是选 soft 和 hard 中的小值
为什么限制和我期望的不一样
我通过 SSH 登陆系统之后,通过 ulimit 发现 fd 的限制居然是 1024,但是我查看了上面的所有地方,都没有发现 1024 的设置,并且都比 1024 大:
[root@liqiang.io]# ulimit -Sn
1024
那么是什么原因?通过搜索资料之后,发现这个原因如果没有指定用户级别的设置(/etc/security/limits.conf
),那么默认的设置就是 1024。
lsof 为什么比 ulimit 多一些
主要原因是 lsof 包含链接的库,可执行文件,内存映射等等,这些不是走的进程的一切皆文件的处理方式。