为什么(或如何)由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手册中所述),而不是文件句柄的数量(不同的进程可以打开相同的文件)。 如果是这种情况,那么只需列出打开文件的数量(在/上擦除,从而排除内存映射文件)是不够的

 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 

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

    我在Linux版本2.6.18-164.el5 – Red Hat 4.1.2-46中testing了这个。 我可以看到每个进程都应用ulimit。

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

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

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

    当多个进程打开的文件总和超过1024时,没有任何错误。我还validation了将不同进程的结果和唯一文件统计在一起的唯一文件数量。 只有当每个进程的计数超过1024时才会出现错误。(java.net.SocketException:进程日志中打开的文件过多)

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

    在过程启动期间的任何时候,限制可能已经改变。 访问/proc/<pid>/limits ,看看这些值是否被改变了。

    @oligofren

    我也进行了一些testing,以确定"open files"是如何"ulimits -Sn"的。

    • 就像链接中提到的海报一样, "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

    以上的说明和我的testing方法/结果

    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 

    "-d '^cwd,^err,^ltx,^mem,^mmap,^pd,^rtd,^txt'"参数指示lsof排除文件描述符types: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"

    编辑—开始snip —

    编辑:我发现以下链接 ,其中指出:

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

    所以如果你的进程使用内存映射文件,你将需要过滤* .so文件。

    另外,Sun的JVM将存储映射jar文件

    内存映射的JAR文件,在这种情况下是保存“JDK类”的文件。 在内存映射JAR时,可以非常有效地访问其中的文件(而不是每次从头开始读取)。 Sun JVM将logging映射类path上的所有JAR; 如果您的应用程序代码需要访问JAR,则还可以对其进行内存映射。

    所以像tomcat / glassfish这样的东西也会显示内存映射的jar文件。 我还没有testing这些计数是否超出了"ulimit -Sn" limit "ulimit -Sn"限制。

    编辑—结束剪报—

    根据经验,我发现"cwd,rtd,txt" 不计入每个进程文件限制(ulimit -Sn)。

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

    "-p __process_id__"参数限制lsof只返回指定的__process_id__信息。 如果你想得到所有进程的计数,请删除它。

    "-a"参数用于ANDselect(即“-p”和“-d”参数)。

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

    我testing使用以下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--- 

    我不得不在perldebugging器中执行脚本,以确保脚本不会终止并释放文件描述符。

    要执行: perl -d test.pl

    在perl的debugging器中,你可以通过inputc并按回车来运行程序,如果你的ulimit -Sn的值是1024 ,你会发现程序在/tmp创buildTest1017.log文件后就停止了。

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

    删除"wc -l"并replace为"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文件是什&#x4E48;file-nrfile-max意思。

    这似乎是你的推理是这样的,“我必须降低这个限制,所以我没有用尽珍贵的描述”。 事实正好相反 – 如果你的服务器用完了文件描述符,你需要这个限制从1,024 提高到更大。 对于一个现实的glassfish实现,32,768是合理的。

    就个人而言,我总是把系统的限制提高到8,192个系统 – 1024个只是荒谬的。 但是,你会想提高glassfish 。 检查/etc/security/limits.conf 。 您可以为用户glassfish运行时添加一个特殊条目。

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