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

oli*_*ren 16 linux glassfish root max-file-descriptors

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

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

lsof -u root |grep /|sort  -k9  |wc -l #prints '1738'
Run Code Online (Sandbox Code Playgroud)

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

lsof -u root |grep /|sort  -k9 -u |wc -l #prints '604'
Run Code Online (Sandbox Code Playgroud)

上面的命令需要 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
Run Code Online (Sandbox Code Playgroud)

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

小智 11

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

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

例如:1024 是限制。启动了多个进程,每个进程打开的文件数使用

ls -l /proc/--$pid--/fd/ | wc -l
Run Code Online (Sandbox Code Playgroud)

当多个进程打开的文件总和超过1024时没有错误。我还验证了唯一文件数,结合了不同进程的结果并计算了唯一文件数。只有当每个进程的计数超过 1024 时,错误才开始出现。( java.net.SocketException: Too many open files in process logs )


Mat*_*Ife 5

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

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


小智 5

@oligofren

我还进行了一些测试以确定如何执行"ulimits -Sn"for 。"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

上述内容和我的测试方法/结果的解释

lsof"-P -M -l -n"的参数只是为了让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
Run Code Online (Sandbox Code Playgroud)

"-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;
Run Code Online (Sandbox Code Playgroud)

我认为"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"参数用于对选择进行AND运算(即“-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---
Run Code Online (Sandbox Code Playgroud)

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

执行:perl -d test.pl

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

如果您现在识别 perl 进程的 pid 并使用上面的lsof命令,您将看到它也输出1024

删除"wc -l"并替换为 a"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-nrfile-max含义。


rnx*_*xrx 0

您想要查看 /proc/sys/fs/file-max 中设置的系统范围限制并在那里进行调整(直到下次重新启动)或在 sysctl.conf 中设置 fs.file-max 以使其永久。这可能会有所帮助 - http://www.randombugs.com/linux/tuning-file-descriptors-limits-on-linux.html