lsof的错误使用场景和查看打开文件数的正确方法

2019-08-20 19:07:36

参考地址 lsof的错误使用场景和查看打开文件数的正确方法

前两天在调查一个"too many open files"的问题,和之前一样,自然而然的用到了lsof,加上一堆漂亮的命令组合来查看哪些程序打开了很多文件。

lsof | awk '{print $2}' | uniq -c | sort -rnk1 | head
啪的一敲回车,很顺利的定位到几个docker内运行的Java进程排在最前,分别都是几万的结果,怀疑是不是docker的问题。很不幸的是,这个调查结果是错的。。。

先说真实的情况,后面再展开分析:

  • 真实的元凶,是一个并没有在上面的命令结果中排在最前面的进程,由于编程的bug,不断的打开同样的文件没有关闭,真正的占用了很多fd。

  • CentOS 7中的lsof是按PID/TID/file的组合显示结果的,上面lsof组合命令显示“打开”了很文件的进程,只是因为进程运行了N个线程,而每个线程都“用到”了M个jar包,并且FD一栏分别为mem和具体fd号都分别显示了一次,就出现了2*N*M——上万条结果。

结论一:
使用lsof查看fd数是不正确的。
尽管网上很多文章教人这么用,但实际上不应该这么做。

这是因为:

  1. lsof的结果包含了并非以fd形式打开的文件,比如用mmap方式访问文件(FD一栏显示为mem),实际并不占用fd。
    其中包括了像.so这样的文件。从结果看.jar文件也是以FD为mem和具体fd编号分别打开了一次。

  2. CentOS 7的lsof(我这里lsof -v的版本号是4.87)是按PID/TID/file的组合对应一行,不是一行一个fd。同一个进程如果多个线程访问同一个文件通常只需要打开一次、占用一个fd,但在lsof中就显示多行。
    如果用lsof -p <pid>,则不按TID显示,结果数少很多。但仍包含了没有使用fd的文件。

结论二:
准确的查看fd使用总数的命令是:
cat /proc/sys/fs/file-nr
或者(结果多的时候运行需要一段时间)
sudo find /proc -print | grep -P '/proc/\d+/fd/'| wc -l

注意如果用
sudo ls -l /proc/*/fd/* | wc -l
结果是不对的,比上面的命令返回结果少很多。原因是实际执行是会把*扩充成具体的目录作为参数,而这个参数长度有限制。

查看具体一个进程号的fd数量是:
ls -l /proc/<pid>/fd | wc -l

查看哪个进程使用的fd最多(再来一路组合拳):
sudo find /proc -print | grep -P '/proc/\d+/fd/'| awk -F '/' '{print $3}' | uniq -c | sort -rn | head

但还要注意上面的命令返回的是系统的fd使用情况,而ulimit的配置是针对单用户的,两者是有区别的。

结论三:
不同版本的lsof输出结果不同。

CentOS 7.3的lsof (我这里是4.87),按PID/TID/file显示。CentOS 6.6的lsof(我这里是4.82),按PID/file显示。结果数相差很大。但lsof -p <pid>的结果是一致的。
这在容器OS版本和宿主机OS版本不同时就需要注意了,在容器里和宿主机上用lsof查看同一进程的结果会很不同,我碰到的就是这种情况。

</br>


之前使用lsof(或lsof -n不解析协议主机名)的相关命令和真实意义:
打开文件总记录数(没太大意义):
lsof | wc -l

lsof -n | wc -l #不解析协议主机名

查看哪些pid使用文件数量最多(其实也没有太大意义):
lsof | awk '{print $2}' | uniq -c | sort -rnk1 | head



  • 2020-04-28 10:48:39

    使用currentColor设置SVG样式

    css自己也又很多变量哦,大家还要多多学习哦。CSS中的变量很有用,因为它们允许我们写更多DRY(不再自我重复,也就是不需要一直写重复内容的代码)代码。它们对于管理和维护大型项目也有非常好用,因为它们包含了大量重复的值。

  • 2020-04-29 20:57:07

    mac卸载avast

    第一种就是卸载。打开主界面,在左上角Avast一栏中选择卸载。卸载完之后要么再装回去,要么去找找别的杀软吧

  • 2020-05-06 14:43:53

    bootstrap @media尺寸

    超大屏,1100分辨率,880分辨率,720分辨率,440分辨率以下

  • 2020-05-06 22:23:15

    使用querySelector添加移除style和class

    document.querySelector(selector).style.styleName = 样式 使用这个方法可以对dom节点添加和移除style样式,其中的styleName可以为css样式的任何值,如:display、color

  • 2020-05-07 13:05:28

    JavaScript中批量设置Css样式

    setAttribute() 方法添加指定的属性,并为其赋指定的值。 如果这个指定的属性已存在,则仅设置/更改值