如何解决 Linux 上的“打开文件过多”错误
已发表: 2022-06-29在 Linux 计算机上,系统资源在用户之间共享。 尝试使用超过您的公平份额,您将达到上限。 您还可能成为其他用户或进程的瓶颈。
共享系统资源
在其他大量工作中,Linux 计算机的内核总是忙于观察谁在使用多少有限的系统资源,例如 RAM 和 CPU 周期。 多用户系统需要持续关注以确保人员和进程不会使用超出适当范围的任何给定系统资源。
例如,对于某人占用如此多的 CPU 时间以致其他人感觉计算机运行缓慢是不公平的。 即使您是唯一使用 Linux 计算机的人,也为您的进程可以使用的资源设置了限制。 毕竟,您仍然只是另一个用户。
一些系统资源是众所周知且显而易见的,例如 RAM、CPU 周期和硬盘空间。 但是还有很多很多的资源需要监控,每个用户——或者每个用户拥有的进程——都有一个设定的上限。 其中之一是进程一次可以打开的文件数。
如果您曾经在终端窗口中看到“打开的文件过多”错误消息或在系统日志中发现它,则表示已达到上限,并且不允许该进程打开更多文件。
这不仅仅是您打开的文件
Linux 可以处理的打开文件的数量存在系统范围的限制。 正如我们将看到的,这是一个非常大的数字,但仍有一个限制。 每个用户进程都有一个他们可以使用的分配。 他们每个人都获得分配给他们的系统总数的一小部分。
实际分配的是一些文件句柄。 每个打开的文件都需要一个句柄。 即使分配相当大,系统范围内的文件句柄也会比您最初想象的更快用完。
Linux 对几乎所有内容进行了抽象,使其看起来好像是一个文件。 有时它们只是简单的旧文件。 但其他操作(例如打开目录)也使用文件句柄。 Linux 使用块特殊文件作为硬件设备的一种驱动程序。 字符特殊文件非常相似,但它们更常用于具有吞吐量概念的设备,例如管道和串行端口。
块特殊文件一次处理数据块,字符特殊文件分别处理每个字符。 这两个特殊文件都只能通过使用文件句柄来访问。 程序使用的库使用文件句柄,流使用文件句柄,网络连接使用文件句柄。
抽象出所有这些不同的需求,使它们以文件的形式出现,从而简化了与它们的交互,并允许管道和流等工作。
您可以看到,Linux 在幕后打开文件并使用文件句柄只是为了运行自己——别管您的用户进程。 打开文件的数量不仅仅是您打开的文件数量。 操作系统中的几乎所有东西都在使用文件句柄。
文件句柄限制
使用此命令可以查看系统范围的最大文件句柄数。
cat /proc/sys/fs/file-max
这返回了一个荒谬的大量 9.2 quintillion。 这是理论系统的最大值。 这是您可以在 64 位有符号整数中保存的最大可能值。 你那台糟糕的电脑是否真的能处理一次打开的这么多文件完全是另一回事。
在用户级别,您可以拥有的打开文件的最大数量没有明确的值。 但是我们可以大致算出来。 要找出您的一个进程可以打开的最大文件数,我们可以使用带有-n
(打开文件)选项的ulimit
命令。
ulimit -n
为了找到用户可以拥有的最大进程数,我们将使用ulimit
和-u
(用户进程)选项。
ulimit -u
将 1024 和 7640 相乘得到 7,823,360。 当然,其中许多进程已经被您的桌面环境和其他后台进程使用。 所以这是另一个理论上的最大值,而且你永远无法实现。
重要的数字是进程可以打开的文件数。 默认情况下,这是 1024。值得注意的是,同时打开同一个文件 1024 次与同时打开 1024 个不同文件相同。 一旦你用完所有的文件句柄,你就完成了。
可以调整进程可以打开的文件数量。 在调整此数字时,实际上需要考虑两个值。 一个是当前设置的值,或者您尝试设置的值。 这称为软限制。 还有一个硬限制,这是您可以将软限制提高到的最高值。
考虑这一点的方式是软限制实际上是“当前值”,而上限是当前值可以达到的最高值。 普通的非 root 用户可以将他们的软限制提高到任何值,直到他们的硬限制。 root 用户可以增加他们的硬限制。
要查看当前的软限制和硬限制,请将ulimit
与-S
(软)和-H
(硬)选项以及-n
(打开文件)选项一起使用。
ulimit -Sn
ulimit -Hn
为了创造一种我们可以看到正在实施的软限制的情况,我们创建了一个重复打开文件直到失败的程序。 然后它在放弃它使用的所有文件句柄之前等待击键。 该程序称为open-files
。
./open-文件
它打开 1021 个文件并在尝试打开文件 1022 时失败。
1024 减去 1021 是 3。其他三个文件句柄发生了什么? 它们用于STDIN
、 STDOUT
和STDERR
流。 它们是为每个进程自动创建的。 它们的文件描述符值始终为 0、1 和 2。
相关:如何使用 Linux lsof 命令
我们可以使用带有-p
(进程)选项的lsof
命令和open-files
程序的进程 ID 来查看这些内容。 方便地,它将其进程 ID 打印到终端窗口。
lsof -p 11038
当然,在实际情况下,您可能不知道哪个进程刚刚吞噬了所有文件句柄。 要开始您的调查,您可以使用此管道命令序列。 它会告诉您计算机上文件句柄最多产的 15 个用户。
lsof | awk '{ 打印 $1 " " $2; }' | 排序-rn | 唯一的-c | 排序-rn | 头 -15
要查看更多或更少的条目,请将-15
参数调整到head
命令。 一旦你确定了这个进程,你需要弄清楚它是否已经失控并打开了太多的文件,因为它失去了控制,或者它是否真的需要这些文件。 如果确实需要它们,则需要增加其文件句柄限制。
增加软限制
如果我们增加软限制并再次运行我们的程序,我们应该会看到它打开了更多文件。 我们将使用ulimit
命令和-n
(打开文件)选项,数值为 2048。这将是新的软限制。
ulimit -n 2048
这次我们成功打开了2045个文件。 正如预期的那样,这比 2048 少 3,因为文件句柄用于STDIN
、 STDOUT
和STDERR
。
进行永久性更改
增加软限制只会影响当前的 shell。 打开一个新的终端窗口并检查软限制。 您会看到它是旧的默认值。 但是有一种方法可以为进程可以拥有的打开文件的最大数量全局设置一个新的默认值,该值是持久的并且在重新启动后仍然存在。
过时的建议通常建议您编辑“/etc/sysctl.conf”和“/etc/security/limits.conf”等文件。 但是,在基于 systemd 的发行版上,这些编辑并不能始终如一地工作,尤其是对于图形登录会话。
此处显示的技术是在基于 systemd 的发行版上执行此操作的方法。 我们需要处理两个文件。 第一个是“/etc/systemd/system.conf”文件。 我们需要使用sudo
。
须藤 gedit /etc/systemd/system.conf
搜索包含字符串“DefaultLimitNOFILE”的行。 从行的开头删除哈希“#”,并将第一个数字编辑为您想要的新进程软限制。 我们选择了 4096。该行的第二个数字是硬限制。 我们没有对此进行调整。
保存文件并关闭编辑器。
我们需要对“/etc/systemd/user.conf”文件重复该操作。
须藤 gedit /etc/systemd/user.conf
对包含字符串“DefaultLimitNOFILE”的行进行相同的调整。
保存文件并关闭编辑器。 您必须重新启动计算机或使用带有daemon-reexec
选项的systemctl
命令,以便重新执行systemd
并获取新设置。
sudo systemctl daemon-reexec
打开终端窗口并检查新限制应该会显示您设置的新值。 在我们的例子中是 4096。
ulimit -n
我们可以通过重新运行我们的文件贪婪程序来测试这是一个实时的、可操作的值。
./open-文件
该程序无法打开文件编号 4094,这意味着 4093 是打开的文件。 这是我们的预期值,比 4096 小 3。
一切都是文件
这就是Linux如此依赖文件句柄的原因。 现在,如果您开始用完它们,您就知道如何增加配额。
相关: Linux 上的标准输入、标准输出和标准错误是什么?