为什么(或如何)root 使用的打开文件描述符的数量超过了 ulimit -n?

为什么(或如何)root 使用的打开文件描述符的数量超过了 ulimit -n?

我们的服务器最近用完了文件描述符,对此我有一些疑问。ulimit -n应该给我打开文件描述符的最大数量。这个数字是 1024。我通过运行检查了打开文件描述符的数量,lsof -u root |wc -l得到了 2500 个 fds。这比 1024 多得多,所以我猜这意味着 1024 是每个进程的数字,而不是每个用户的数字,就像我所想的那样。好吧,我运行lsof -p$PidOfGlassfish|wc -l后得到了 1300。这是我不明白的部分。如果ulimit -n不是每个用户或每个进程的最大进程数,那么它有什么用呢?它不适用于 root 用户吗?如果是这样,那么我怎么才能得到关于文件描述符用尽的错误消息呢?

编辑:我能理解的唯一方法ulimit -n是,如果它应用的是打开文件的数量(如 bash 手册中所述),而不是文件句柄的数量(不同的进程可以打开同一个文件)。如果是这种情况,那么只需列出打开文件的数量(在 '/' 上进行 grep,从而排除内存映射文件)即可不是足够:

lsof -u root |grep /|sort  -k9  |wc -l #prints '1738'

要实际查看打开的文件数,我需要过滤名称列,仅打印唯一条目。因此,以下内容可能更正确:

lsof -u root |grep /|sort  -k9 -u |wc -l #prints '604'

上述命令期望 lsof 的输出格式如下:

java      32008 root  mem       REG                8,2 11942368      72721 /usr/lib64/locale/locale-archive
vmtoolsd   4764 root  mem       REG                8,2    18624     106432 /usr/lib64/open-vm-tools/plugins/vmsvc/libguestInfo.so

这至少给了我小于 1024 的数字(报告的数字ulimit -n),所以这似乎是朝着正确方向迈出的一步。“不幸的是”我没有遇到任何文件描述符用尽的问题,所以我很难验证这一点。

答案1

我在 Linux 版本 2.6.18-164.el5 - Red Hat 4.1.2-46 中对此进行了测试。我可以看到 ulimit 是针对每个进程应用的。

该参数在用户级别设置,但适用于每个过程。

例如:1024 是限制。启动了多个进程,并使用以下方法计算每个进程打开的文件数:

ls -l /proc/--$pid--/fd/ | wc -l

当多个进程打开的文件总数超过 1024 时,没有出现错误。我还验证了唯一文件计数,结合了不同进程的结果并计算了唯一文件数。只有当每个进程的计数超过 1024 时,错误才会开始出现。(java.net.SocketException:进程日志中打开的文件太多)

答案2

ulimit 用于文件句柄。它适用于文件、目录、套接字、管道 epoll、eventfds、timerfds 等等。

在进程启动期间的任何时候,限制都可能发生变化。访问/proc/<pid>/limits并查看值是否已更改。

答案3

@oligofren

我还进行了一些测试来确定如何"ulimits -Sn"执行"open files"

  • 就像海报一样选择提到关联,ulimit"open files"确实适用于每个进程。要查看进程的当前限制,请执行以下操作:

    cat /proc/__process_id__/limits

  • 要确定进程打开了多少个文件,需要使用以下命令:

    lsof -P -M -l -n -d '^cwd,^err,^ltx,^mem,^mmap,^pd,^rtd,^txt' -p __process_id__ -a | awk '{if (NR>1) print}' | wc -l

以上内容及我的测试方法/结果的解释

论据"-P -M -l -n"lsof只是为了制造lsof尽可能快地操作。随意取出。

-P - inhibits the conversion of port numbers to port names for network files
-M - disable reporting of portmapper registrations for local TCP, UDP and UDPLITE ports
-l - inhibits the conversion of user ID numbers to login names
-n - inhibits the conversion of network numbers to host names for network files

"-d '^cwd,^err,^ltx,^mem,^mmap,^pd,^rtd,^txt'"参数指示lsof排除以下类型的文件描述符:cwd/err/ltx/mem/mmap/pd/rtd/txt。

来自 lsof 手册页:

   FD         is the File Descriptor number of the file or:

                   cwd  current working directory;
                   Lnn  library references (AIX);
                   err  FD information error (see NAME column);
                   jld  jail directory (FreeBSD);
                   ltx  shared library text (code and data);
                   Mxx  hex memory-mapped type number xx.
                   m86  DOS Merge mapped file;
                   mem  memory-mapped file;
                   mmap memory-mapped device;
                   pd   parent directory;
                   rtd  root directory;
                   tr   kernel trace file (OpenBSD);
                   txt  program text (code and data);
                   v86  VP/ix mapped file;

我认为"Lnn,jld,m86,tr,v86"不适用于 Linux,因此没有费心将它们添加到排除列表中。我不确定"Mxx"

如果您的应用程序使用内存映射文件/设备,那么您可能需要将"^mem""^mmap"从排除列表中删除。

编辑---开始剪辑---

编辑:我发现了以下内容关联这表明:

内存映射的 .so 文件在技术上与应用程序控制的文件句柄不同。/proc//fd 是打开文件描述符的测量点

因此,如果您的进程确实使用内存映射文件,则您将需要过滤掉*.so 文件。

此外,Sun 的 JVM 将内存映射 jar 文件

内存映射的 JAR 文件,在本例中是保存“JDK 类”的文件。当您对 JAR 进行内存映射时,您可以非常高效地访问其中的文件(而不是每次都从头开始读取)。Sun JVM 将对类路径上的所有 JAR 进行内存映射;如果您的应用程序代码需要访问 JAR,您也可以对其进行内存映射。

因此,像 tomcat/glassfish 这样的程序也会显示内存映射的 jar 文件。我已经未测试这些是否算入"ulimit -Sn"限制。

编辑---结束剪辑---

"cwd,rtd,txt"从经验上来说,我发现不算关于每个进程文件限制(ulimit -Sn)。

我不确定是否"err,ltx,pd"计入文件限制,因为我不知道如何创建这些描述符类型的文件句柄。

"-p __process_id__"参数限制lsof仅返回指定信息__process_id__。如果您想要获取所有进程的计数,请删除该参数。

"-a"论点用于选择(即“-p”和“-d”参数)。

"awk '{if (NR>1) print}'"语句用于跳过lsof在其输出中打印的标题。

我使用以下 perl 脚本进行了测试:

File: test.pl
---snip---
#!/usr/bin/perl -w
foreach $i (1..1100) {
  $FH="FH${i}";
  open ($FH,'>',"/tmp/Test${i}.log") || die "$!";
  print $FH "$i\n";
}
---snip---

我必须在 perl 调试器中执行该脚本以确保该脚本不会终止并释放文件描述符。

执行:perl -d test.pl

在 perl 的调试器中,您可以通过输入来运行该程序c并按下回车键,如果你的ulimit -Sn值为1024,你会发现程序Test1017.log在创建文件后停止/tmp

如果你现在识别 perl 进程的 pid 并使用上述lsof命令,你会看到它还会输出1024

删除"wc -l"并替换为,"less"以查看计入的文件列表1024限制。删除"-d ^....."参数,以查看cwd,txtrtd描述符没有计入限制。

如果你现在运行"ls -l /proc/__process_id__/fd/ | wc -l",你会看到一个值1025返回。这是因为在其输出中ls添加了一个"total 0"被计数的标头。

笔记:

要检查操作系统是否耗尽文件描述符,最好比较的价值:

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

cat /proc/sys/fs/file-max

https://www.kernel.org/doc/Documentation/sysctl/fs.txt记录什么file-nr以及file-max含义。

答案4

您的推理似乎是这样的:“我必须降低该限制,这样我才不会用完宝贵的描述符”。事实恰恰相反——如果您的服务器用完了文件描述符,您需要增加该限制从 1,024 到更大的值。对于实际glassfish实现,32,768 是合理的。

就我个人而言,我总是将系统范​​围的限制提高到 8,192 左右——1,024 简直太荒谬了。但你会想提高得glassfish更高。检查/etc/security/limits.conf。您可以为用户glassfish运行方式添加一个特殊条目。

相关内容