概述

在高负荷的 Linux 服务器或者代码出现了 Bug 的环境中经常出现 “too many open files”(以下简写为 TMOF) 的错误,这意味着一个进程已经打开太多的文件(文件描述符),无法再打开新的文件了。在 Linux 中,每个进程或用户的最大打开文件限制是有默认设置的,而且数值相当小。

在这篇文章中,我将介绍如何在 Linux 中检查当前最大打开文件的限制,以及如何为整个主机、单个服务或当前会话改变它。

TMOF 错误以及打开文件的限制

首先,让我们看看 “too many open files” 的错误出现在哪里。最常见的是在安装了 web 服务(NGINX/httpd )或数据库服务(MySQL/MariaDB/PostgreSQL)的机器上,当读取大量的日志时,就会出现这种情况。例如,当 Nginx 网络服务器超过 最大可打开文件 的限制时,你会看到一个错误:

  1. socket () failed (29: Too many open files) while connecting to upstream
图 1:Nginx 出现 too many open files

使用这个命令,你可以得到系统可以打开的最大文件描述符的数量:

  1. [[email protected].io]# cat /proc/sys/fs/file-max
  2. 22854988

当前用户的打开文件限制是 1024,你可以通过这条命令检查:

  1. [[email protected].io]# ulimit -n
  2. 1024

其实这里有两种限制类型:硬限制和软限制,用户可以改变软限制(但是,软限制值不能超过硬限制),只有特权用户或根用户可以修改硬限制值。要显示软限制,可以运行以下命令:

  1. [[email protected].io]# ulimit -Sn
  2. 65535

相应地,你可以通过这个命令查看硬限制:

  1. [[email protected].io]# ulimit -Hn
  2. 65535

增加系统最大文件限制数

可以通过改变 Linux 操作系统中的限制来允许所有的服务打开大量的文件。为了让新的设置持久化,防止它们在服务器或会话重启后被重置,必须对 /etc/security/limits.conf 做出修改,具体为在这个文件中添加这些行:

  1. [[email protected].io]# tail -2 /etc/security/limits.conf
  2. root soft nofile 65535
  3. root hard nofile 65535

增加单个服务的最大文件限制数

毕竟增加整个系统的文件描述符数量不是一个很好的事情,如果可以针对服务来控制的话,就更好了,事实上也是可以做的,这里我是使用 systemd 来做的:

  1. [[email protected].io]# grep -A 2 Service /usr/lib/systemd/system/nginx.service
  2. [Service]
  3. LimitNOFILE=1024
  4. LimitNOFILESoft=1024
  5. [[email protected].io]# systemctl daemon-reload
  6. [[email protected].io]# systemctl restart nginx

验证一下文件的最大打开文件数:

  1. [[email protected].io]# cat /proc/230490/limits | grep "Max open files"
  2. Max open files 1024 4096 files

修改当前会话的最大文件限制数

修改当前会话的文件描述符限制数可以很简单地通过 ulimit 命令做到:

  1. [[email protected].io]# ulimit -n 2048

但是,很明显,在重新登陆之后,这个配置就会失效了,被恢复成 /etc/security/limits.conf 中的值,如果也想持久化一把,还可以这么改:

  1. [[email protected].io]# grep "fs.file-max" /etc/sysctl.conf
  2. fs.file-max = 4096
  3. [[email protected].io]# sysctl -p

归纳总结

这三个层级的先后顺序是:

一些问题

Soft 和 Hard 的区别是啥

没有本质区别,但是有些许的权限区别:

为什么限制和我期望的不一样

我通过 SSH 登陆系统之后,通过 ulimit 发现 fd 的限制居然是 1024,但是我查看了上面的所有地方,都没有发现 1024 的设置,并且都比 1024 大:

  1. [[email protected].io]# ulimit -Sn
  2. 1024

那么是什么原因?通过搜索资料之后,发现这个原因如果没有指定用户级别的设置(/etc/security/limits.conf),那么默认的设置就是 1024。

lsof 为什么比 ulimit 多一些

主要原因是 lsof 包含链接的库,可执行文件,内存映射等等,这些不是走的进程的一切皆文件的处理方式。

Ref