我们的服务器最近用完了文件描述符,对此我有一些疑问。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,txt
和rtd
描述符没有计入限制。
如果你现在运行"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
运行方式添加一个特殊条目。