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-01-10 21:59:08

    supervisor 永不挂掉的进程 安装以及使用

    在使用Tp的消息队列 think-queue的时候进程意外结束了!导致项目没法运行; 所以用到了supervisor ; 接下来跟大家分享一下自己的使用心得以及安装到使用的方法;

  • 2020-01-10 22:00:23

    又一款内网穿透工具搭建

    最近一个项目需要用到将订单发布到第三方平台,之后要是有人购买他们会请求我们这边的接口来改变订单状态等! 由于本地开发,测试的时候,他们那边请求我们这边接口的时候没法访问内网 所以要用到内网穿透 当然现在也有很多更简单的 比如花生壳就是很好的!

  • 2020-01-13 11:14:43

    p标签中的文本换行显示空白

    white-space: 如何处理元素中的空白,normal: 默认, 被浏览器忽略空白 pre: 空白被浏览器保留. nowrap: 文本不会换行, 会在同一行上继续, 一直走到需要换行为止 pre-wrap: 保留空白符序列, 但正常换行 pre-line: 合并空白符序列, 但正常换行 inherit: 从父元素继承white-space这个属性

  • 2020-01-13 11:26:40

    Ueditor报错not import language file vue

    解决办法,手动加载语言文件,提供了手动加载语言文件,避免ie下有时会因语言文件加载失败导致编辑器加载失败,提示"not import language file"的错误

  • 2020-01-13 11:30:56

    解决Uncaught SyntaxError: Unexpected token <

    今天写代码的时候发现了"Uncaught SyntaxError: Unexpected token <" <html>的js错误,而且还是html的第一行,我就蒙了,这第一行有啥问题啊,还有一个“<”符号,我以为是我的网页标签没有闭包,所以检查了一下,都不是啊!错误如图。

  • 2020-01-13 14:53:51

    sass中使用calc计算表达式变量问题

    calc()是css的一个函数,可用于元素计算长度,比如div宽度想要减去一个固定宽度后并自适应,可以写为calc(100% - 60px) 注意“-”两边有空格 sass已经是常用的预编译语言,允许使用变量等规则,如果上边写到60px是一个变量,这个表达怎么写呢

  • 2020-01-13 21:01:04

    vue路由的异步加载(懒加载)方法

    vue本身不多介绍。直接说问题,因为vue的所有路由都是加载在一个app.js里的,如果项目巨大,那么首页加载会是灾难。所以我们就需要把某些路由用异步加载(懒加载)的方式进行加载.