Contents

修复Linux中的“打开的文件太多”错误

1. 概述

在使用 Linux 服务器时,我们可能会遇到“打开的文件过多”错误。在本文中,我们将讨论此错误的含义以及如何修复它。

2. 什么是文件描述符?

从常规数据到网络套接字,Linux 中的一切都是文件! 文件描述符是 Linux 中打开文件的非负整数标识符每个进程都有一个打开文件描述符表,在打开一个新文件时会附加一个新条目

例如,当我们cat 文件时会发生什么?它打开通过open() 系统调用作为参数传递的文件,并为其分配一个文件描述符。然后,它通过文件描述符与文件交互——在这种情况下,只是为了显示它的内容——最后,通过close() 系统调用关闭它。

一个进程默认打开 三个文件描述符,用0表示stdin1表示stdout2表示stderr

3. 检查打开的文件描述符

我们可以检查各种代理使用的文件描述符的数量,以确定我们的资源在哪里被使用。

3.1. 全球使用

如果我们想检查系统上打开的文件描述符的总数,我们可以使用awk 单行器在*/proc/sys/fs/file-nr*文件的第一个字段中找到它:

$ awk '{print $1}' /proc/sys/fs/file-nr
2944

3.2. 每个进程的使用

我们可以使用lsof 命令来检查进程的文件描述符使用情况。我们以caddy web 服务器为例:

$ sudo lsof -p $(pidof caddy)
COMMAND PID  USER   FD      TYPE  DEVICE SIZE/OFF    NODE NAME
caddy   135 caddy  cwd       DIR   254,1     4096 1023029 /etc/sv/caddy
caddy   135 caddy  rtd       DIR   254,1     4096       2 /
caddy   135 caddy  txt       REG   254,1 33595392 1542819 /usr/bin/caddy
caddy   135 caddy    0u      CHR     5,1      0t0      12 /dev/console
caddy   135 caddy    1u      CHR     5,1      0t0      12 /dev/console
caddy   135 caddy    2w     FIFO     0,9      0t0     119 pipe
caddy   135 caddy    3u  a_inode    0,10        0       6 [eventpoll:2,4,6,7,8,9,10,11,13,15,16,17,18,19,20,21,22,23,24,25,27,28,29,30,31,32,33,34]
caddy   135 caddy    4r     FIFO     0,9      0t0     171 pipe
caddy   135 caddy    5w     FIFO     0,9      0t0     171 pipe
caddy   135 caddy    6u     IPv4     234      0t0     TCP localhost.localdomain:2019 (LISTEN)
caddy   135 caddy    7u     IPv6     240      0t0     TCP *:443 (LISTEN)
caddy   135 caddy    8u     IPv6     241      0t0     TCP *:80 (LISTEN)
caddy   135 caddy    9u     IPv6 3819034      0t0     TCP 66.46.134.82.blogdemo.com:443->google.com:53222 (ESTABLISHED)

我们可以在NAME列下看到给定文件描述符所属的确切文件,而它的类型位于TYPE列下。查看以IPv6为类型的条目,我们可以得出结论,即使网络套接字也占用文件描述符,就像普通文件一样。

4. 文件描述符限制

一个进程对于它一次可以打开的文件描述符的数量有一定的限制。一种是软限制,任何非特权用户都可以更改,并且永远不能超过硬限制。非特权用户可以降低硬限制但不能再次提高它,而特权用户(例如root )可以根据需要提高和降低它。

4.1. 每会话限制

我们使用带有*-Sn标志的ulimit 命令检查软限制,使用-Hn*标志检查当前会话的硬限制:

$ ulimit -Sn
1024
$ ulimit -Hn
4096

4.2. 每进程限制

我们可以通过procfs文件系统检查一个进程的限制,给定它的 PID :

$ pid=31540
$ grep "Max open files" /proc/$pid/limits
Max open files            1024                 4096                 files

第二个和第三个字段分别对应软限制和硬限制。

4.3. 全局限制

**可以由所有进程组合打开的文件描述符总数存在系统范围的限制。*此限制存储在/proc/sys/fs/file-max*文件中:

$ cat /proc/sys/fs/file-max
1634185

5. 增加文件描述符限制

现在我们已经很好地理解了“打开的文件太多”错误背后的想法,让我们来看看解决它的各种方法。我们可以使用上一节中提到的命令来验证这些更改。在本节的示例中,我们使用500000来指代所需的限制。

5.1. 临时(每个会话)

让我们尝试使用ulimit -n命令为当前会话配置限制:

$ ulimit -n 4096
$ ulimit -n
4096
$ ulimit -n 8192
sh: error setting limit: Operation not permitted

**我们不能将限制设置为 4096 以上,因为在这种情况下这是硬限制。**并且如上所述,只有特权用户才能更改硬限制。

5.2. 按用户

我们可以通过在/etc/security/limits.conf*文件中添加几行*并重新登录来全局更改所有进程的软限制和硬限制:

*         hard    nofile      500000
*         soft    nofile      500000
root      hard    nofile      500000
root      soft    nofile      500000

在第一个字段中,我们指定限制影响的用户。我们使用glob 为系统上的所有用户设置了限制。

此外,在某些系统上,我们可能需要在/etc/pam.d/common-session中添加一行:**

session required pam_limits.so

5.3. 按服务

在基于systemd 的发行版上,我们使用systemctl 命令来配置特定服务的限制。以apache为例,我们用tee 为它创建一个filelimit.conf文件:

$ sudo mkdir -p /etc/systemd/system/apache.service.d/
$ sudo tee /etc/systemd/system/apache.service.d/filelimit.conf <<EOF
[Service]
LimitNOFILE=500000
EOF

现在,我们只需重新加载配置并重新启动服务:

$ sudo systemctl daemon-reload
$ sudo systemctl restart apache.service

5.4. 全球范围内

早些时候,我们在*/proc/sys/fs/file-max文件中检查了系统范围的聚合限制。我们可以通过在/etc/sysctl.conf*文件中添加一行来使用sysctl 对其进行配置:

fs.file-max = 500000

最后,我们需要运行sysctl -p命令重新加载配置文件。