lsof
2013-02-04 10:42:14

lsof(list open file)是一个列出当前系统打开文件的工具,在linux/unix系统中,任何事物都是以文件的形式存在,通过文件不仅可以访问常规数据,还可以访问网络连接和硬件。所以如果传输控制协议(TCP)和用户数据包协议(UDP)套接字等,系统在后台都为该应用分配一个文件描述符,无论这个文件的本质如何,该文件描述符为应用程序与基础操作系统之间提供了通用的接口。lsof命令的原始功能是列出打开的文件的进程,包括设备在内也是以文件的形式存在的。lsof输出包含很多列,例如:PID、USER、FD 和 TYPE等。

lsof command (LiSt Open Files) is used to list all open files on a Linux system.

直接输入lsof的部分输出每行显示一个打开的文件,若不指定条件默认将显示所有进程打开的所有文件,lsof输出各列信息的意义如下:

lsof -i :81
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
httpd 2278 root4u  IPv6 543252  0t0  TCP *:81 (LISTEN)
httpd 2279 freeoa4u  IPv6 543252  0t0  TCP *:81 (LISTEN)
httpd 2279 freeoa   10u  IPv6 568343  0t0  TCP localhost.localdomain:81->10.206.19.175:38649 (ESTABLISHED)

说明:
每行打开一个文件,如果不指定条件,将显示所有进程打开的文件。lsop输出各列信息如下:
COMMAND:进程的名称
PID:进程标识符
USER:进程所有者
FD:文件描述符,应用程序通过文件描述符识别该文件。如cwd、txt等 TYPE:文件类型,如DIR、REG等
TYPE:文件类型,如DIR、REG等
DEVICE:指定磁盘的名称
SIZE:文件的大小
NODE:索引节点(文件在磁盘上的标识)
NAME:打开文件的确切名称

其中FD 列中的文件描述符cwd 值表示应用程序的当前工作目录,这是该应用程序启动的目录,除非它本身对这个目录进行更改,txt 类型的文件是程序代码,如应用程序二进制文件本身或共享库,如上列表中显示的 /sbin/init 程序。其次数值表示应用程序的文件描述符,就是打开文件时返回的一个整数。u表示该文件并处于打开并读写模式。

其次数值表示应用程序的文件描述符,这是打开该文件时返回的一个整数。如上的最后一行文件/dev/initctl,其文件描述符为 10。u 表示该文件被打开并处于读取/写入模式,而不是只读 ® 或只写 (w) 模式。同时还有大写 的W 表示该应用程序具有对整个文件的写锁。该文件描述符用于确保每次只能打开一个应用程序实例。初始打开每个应用程序时,都具有三个文件描述符,从 0 到 2,分别表示标准输入、输出和错误流。所以大多数应用程序所打开的文件的 FD 都是从 3 开始。

与 FD 列相比,Type 列则比较直观。文件和目录分别称为 REG 和 DIR,而CHR 和 BLK,分别表示字符和块设备;或者 UNIX、FIFO 和 IPv4,分别表示 UNIX 域套接字、先进先出 (FIFO) 队列和网际协议 (IP) 套接字。

FD - File descriptor

FD 列包含这样一些值
cwd - Current working directory
txt - Text file
mem - Memory Mapped file
mmap - Memory Mapped device
Number - It represent the actual file descriptor. For example, 0u, 1w and 3r

r 是读的意思,w 是写,u 代表读写。

Type 代表文件类型,例如:
>REG - Regular file
>DIR - Directory
>CHR - Character special file
>FIFO - First in first out

Linux/Unix 的文件类型有以下 7 种:
Regular file
Directory
Symbolic link
Named pipe
Socket
Device file
Door

语法:lsof [-?ahlnNPRsv][-c c][+|-d d][+|-D D][-g [s]] [+|-L [I]][-p s][+|-r [t]][-u s][names]

参数说明:若没有加上任何参数,lsof会列出所有被程序开启的文件。
-? -h 这两个参数意思相同,显示出lsof的使用说明
-a 参数被视为AND,会影响全部的参数
-C c 显示出以字符或字符串c开头的命令程序开启的文件,如:$ lsof -C init
+d s 在文件夹s下搜寻,此参数不会继续深入搜寻此文件夹。如显示在/usr/local下被程序开启的文件:$ lsof +d /usr/local
+D D 同上,但是会以该文件为基础往下全部搜寻,这样花费较大的CPU时间,请谨慎使用
-d s 此参数以file descriptor(FD)值显示结果,可以采用范围(1-3)或个别,如显示FD为4的进程:$ lsof -d 4
-g [s] 以程序的PGID显示,也可以采用范围或个别表示,若没有特别指定,则显示全部,如显示PGID为6的进程:$ lsof -g 6
-i [i] 用以监听有关的任何符合的地址,若没有相关地址被指定,则监听全部 


语法:lsof -i [46][protocol][@hostname|hostaddr][:serivce|port]
说明:46 - IPv4 or IPv6
protocol TCP or UDP
hostname internet host name
hostaddr IPv4地址
service /etc/service中的service name
port 端口号
-l 此参数禁止将user ID 转换为登录的名称,默认是登录名称
+|-L [l] +或-表示开启或关闭显示文件连接数,如果只有单纯的+L,后面没有任何数字,则表示显示全部,如果后面有数字,只有文件连接数少于该数字的会被列出
-n 不将IP地址转换为hostname,预设是转换的
-N 显示NFS的文件
-p s 以PID作为显示的依据
-P 此参数禁止将port number转换为service name,预设为转换
+|-r [t] 控制lsof不断重复执行,t为15秒,也就是说每隔15秒再重复执行 +r 一直执行,直到没有文件被显示 -r 永远不断的执行,直到收到中断讯号(ctrl+c)
-R 此参数增列出PID的子程序,也就是PPID
-s 列出文件的大小,若该文件没有大小,则留下空白
-u s 列出login name或UID为的程序
-v 显示lsof的版本信息


用lsof列出当前正在被打开的文件

一、lsof有什么用
lsof可以找出被特定进程所打开的文件、目录、套接字、设备。比如有时我们不能umount掉一个分区时,我们会需要检查,是哪些进程在使用当前的分区以便找出进程后将它关闭。

查看某路径目录被进程打开的情况
lsof +d 'directory' (列出在目录下打开的文件)
lsof +D 'directory' (递归地列出在目录下打开的文件,会有比较多的时耗)


一个与脚本相关的例子:使用ftp接收别人上传的文件,有程序被放到crond中定时进行处理,此时就有一个问题;我们如何得知当前被处理的文件是否上传完了,如果上传的是一张图片,且正在传输中我们就进行了处理,则我们会得到一张残缺不全的图片。解决方法:用lsof对文件进行检查,如果有进程在访问,就表示还未上传完成,可以先跳过当前文件。

二、lsof的用法举例
1,检查目录和文件
# umount /mnt/cdrom
umount: /mnt/cdrom: device is busy.
(In some cases useful info about processes that use the device is found by lsof(8) or fuser(1))

在做umount设备时, device is busy是令人头痛的提示,大家看我使用的是fedora 10,umount会自动给出让大家用lsof或fuser进行检测的信息
# lsof /mnt/cdrom
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
bash    2705 root  cwd    DIR    8,1     8192    1 /mnt/cdrom
lsof    2756 root  cwd    DIR    8,1     8192    1 /mnt/cdrom
lsof    2757 root  cwd    DIR    8,1     8192    1 /mnt/cdrom

可以看到,有3个进程在访问/mnt/cdrom,程序分别是 bash和lsof,lsof给出的信息很全,还列出了进程的id及用户,因为我们当前就处在cdrom目录下,所以bash也是其中打开cdrom目录的一个进程。

下面我们从目录下退出:
# cd
# umount /mnt/cdrom

这个例子提醒大家:如果umount一个分区时,lsof列出的进程中有bash,通常是有用户登录在此目录下,通知用户让他从目录下cd出来就可以了,还有一种情况:如果是apache在访问当前分区怎么办?像apache或其他daemon在访问分区,很简单,只需要停止daemon就可以了。

2,有没有这种可能:我们用lsof看不到进程,但是在做umount时系统仍然提示 device is busy?有这种可能:
# umount /store2
umount: /store2: device is busy
# lsof /store2

很奇怪,我们用lsof看不到访问/store2的进程,但umount时系统却告诉我们说:
device is busy
为什么会这样?是因为有可能nfs正在使用此进程查看/etc/exports,此目录便位于其中。
# cat /etc/exports
/store2 201.103.105.x(rw,sync,no_root_squash)
/store2 201.103.105.y(rw,sync,no_root_squash)

此时如何处理?把/etc/exports中的nfs出去的目录注释掉,用#加在每一行前面即可,然后无需重启 nfs,执行 exportfs -rv 即可。

3,根据进程id得到其打开的文件列表:
我们查看一个apache进程所打开的文件列表:
# lsof -p 2759
COMMAND  PID   USER   FD   TYPE    DEVICE      SIZE      NODE NAME
httpd   2759 nobody  cwd    DIR     253,0      4096         2 /
httpd   2759 nobody  rtd    DIR     253,0      4096         2 /
httpd   2759 nobody  txt    REG     253,0   1885982   4622117 /usr/local/apache2/bin/httpd
httpd   2759 nobody  mem    REG     253,0    334391   4621269 /usr/local/apache2/lib/libaprutil-0.so.0.9.12
httpd   2759 nobody  mem    REG     253,0    374273   4621265 /usr/local/apache2/lib/libexpat.so.0.1.0
httpd   2759 nobody  mem    REG     253,0    578133   4621251 /usr/local/apache2/lib/libapr-0.so.0.9.12
httpd   2759 nobody  mem    REG     253,0     21546   4407507 /usr/lib64/gconv/gconv-modules.cache
httpd   2759 nobody  mem    REG     253,0     30070   9519340 /lib64/libcrypt-2.3.4.so
httpd   2759 nobody  mem    REG     253,0  18848742   4622118 /usr/local/apache2/modules/libphp5.so
......
它打开的文件很多,这里不再全列出。

4,列出某个终端上的用户正在做什么?
例如:我们先登录到一个终端上(先用tty得到当前终端)
# tty       
/dev/pts/2
# cat
然后登录到另一终端:
# lsof /dev/pts/2
COMMAND   PID USER   FD   TYPE DEVICE SIZE NODE NAME
cat       514 root    0u   CHR  136,2         4 /dev/pts/2
cat       514 root    1u   CHR  136,2         4 /dev/pts/2
cat       514 root    2u   CHR  136,2         4 /dev/pts/2
bash    32551 root    0u   CHR  136,2         4 /dev/pts/2
bash    32551 root    1u   CHR  136,2         4 /dev/pts/2
bash    32551 root    2u   CHR  136,2         4 /dev/pts/2
bash    32551 root  255u   CHR  136,2         4 /dev/pts/2

5,列出所有命令名字相同的进程所打开的全部文件
例如:apache的进程名命令全部都是httpd,我们列出apache打开的所有进程时,可以使用 -c参数。
# lsof -c httpd
COMMAND   PID   USER   FD   TYPE     DEVICE       SIZE       NODE NAME
httpd   15722   root  cwd    DIR        8,8       4096          2 /
httpd   15722   root  rtd    DIR        8,8       4096          2 /
httpd   15722   root  txt    REG        8,2    1701872    1087581 /usr/local/apache/bin/httpd
httpd   15722   root  mem    REG        8,8     103044      31946 /lib/ld-2.3.2.so
httpd   15722   root  mem    REG        8,8      23668      31955 /lib/libcrypt-2.3.2.so
httpd   15722   root  mem    REG        8,2     877128      69041 /usr/lib/libsablot.so.0.100.1
httpd   15722   root  mem    REG        8,2     442752      69039 /usr/lib/libjs.so
httpd   15722   root  mem    REG        8,2     130104      64938 /usr/lib/libexpat.so.0.4.0
httpd   15722   root  mem    REG        8,2      52616      64985 /usr/lib/libz.so.1.1.4
httpd   15722   root  mem    REG        8,2     136068      66295 /usr/lib/libcurl.so.2.0.2
httpd   15722   root  mem    REG        8,8      76552      31977 /lib/libresolv-2.3.2.so
httpd   15722   root  mem    REG        8,8     211948      95819 /lib/tls/libm-2.3.2.so
     ......
进程太多,不一一列举

6,根据目录列出指定目录下被进程打开的文件或目录
先看两个参数:
+d,只搜索当前目录下被打开的文件或目录
+D,递归搜索当前目录下被打开的文件或目录(注意此参数会导至速度变慢)
示例:
# mkdir abc
# cd abc/
在另一终端执行lsof
# lsof +d /store2/yc/
COMMAND   PID USER   FD   TYPE DEVICE SIZE      NODE NAME
bash    32473 root  cwd    DIR   8,16 4096 111329281 /store2/yc/abc

下面我们再深入一层目录
# mkdir def
# cd def
还是到另一终端执行lsof
# lsof +d /store2/yc/
#
看不到进程了,因为def不再是直接在/store2/yc目录下,而是更下一层,则此时需要+D
# lsof +D /store2/yc/
COMMAND   PID USER   FD   TYPE DEVICE SIZE      NODE NAME
bash    32473 root  cwd    DIR   8,16 4096 111345665 /store2/yc/abc/def

# lsof +D /data/hdfs/                                                                                                                                                                      
COMMAND   PID USER   FD   TYPE DEVICE  SIZE/OFF       NODE NAME
java    26800 hdfs  755u   REG 252,17       267 4446163739 /data/hdfs/data/current/BP-1475711879-192.168.9.16-1568963240961/current/rbw/blk_1091296074_17555409.meta
java    26800 hdfs  757u   REG 252,17    187875 4299798806 /data/hdfs/data/current/BP-1475711879-192.168.9.16-1568963240961/current/rbw/blk_1091272354_17531689.meta
java    26800 hdfs  759uW  REG 252,17        20 2147485912 /data/hdfs/data/in_use.lock
java    26800 hdfs  766u   REG 252,17  24047014 4299782753 /data/hdfs/data/current/BP-1475711879-192.168.9.16-1568963240961/current/rbw/blk_1091272354
java    26800 hdfs  769u   REG 252,17     32921 4446163726 /data/hdfs/data/current/BP-1475711879-192.168.9.16-1568963240961/current/rbw/blk_1091296074
java    26800 hdfs  771u   REG 252,17       299 4809806155 /data/hdfs/data/current/BP-1475711879-192.168.9.16-1568963240961/current/rbw/blk_1090956733_17216068.meta
java    26800 hdfs  777u   REG 252,17     36893 4809728597 /data/hdfs/data/current/BP-1475711879-192.168.9.16-1568963240961/current/rbw/blk_1090956733
...
java    26800 hdfs  892u   REG 252,17   1380467 4385996909 /data/hdfs/data/current/BP-1475711879-192.168.9.16-1568963240961/current/rbw/blk_1091296115_17555450.meta
java    26800 hdfs  895u   REG 252,17 176698880 4310686203 /data/hdfs/data/current/BP-1475711879-192.168.9.16-1568963240961/current/rbw/blk_1091296115
java    28446 yarn  187r   REG 252,17    113464 4333929913 /data/hdfs/data/current/BP-1475711879-192.168.9.16-1568963240961/current/finalized/subdir10/subdir13/blk_1091202465
java    28446 yarn  188r   REG 252,17       895 4333929914 /data/hdfs/data/current/BP-1475711879-192.168.9.16-1568963240961/current/finalized/subdir10/subdir13/blk_1091202465_17461800.meta
java    28446 yarn  189r   REG 252,17    857970 4441866501 /data/hdfs/data/current/BP-1475711879-192.168.9.16-1568963240961/current/finalized/subdir11/subdir18/blk_1091293800
...
java    28561 hive  636r   REG 252,17      3023 4300559098 /data/hdfs/data/current/BP-1475711879-192.168.9.16-1568963240961/current/finalized/subdir25/subdir24/blk_1088026732
java    28561 hive  642r   REG 252,17        31 4300559118 /data/hdfs/data/current/BP-1475711879-192.168.9.16-1568963240961/current/finalized/subdir25/subdir24/blk_1088026732_14286014.meta

# lsof +d /data/hdfs/data/
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF       NODE NAME
java    26800 hdfs  759uW  REG 252,17       20 2147485912 /data/hdfs/data/in_use.lock


7,查看打开某个端口的所有进程
# lsof -i :80
COMMAND   PID   USER   FD   TYPE      DEVICE SIZE NODE NAME
httpd   15722   root   22u  IPv4  1863715557       TCP *:http (LISTEN)
httpd   28161 nobody   22u  IPv4  1863715557       TCP *:http (LISTEN)
httpd   28162 nobody   22u  IPv4  1863715557       TCP *:http (LISTEN)
httpd   28163 nobody   22u  IPv4  1863715557       TCP *:http (LISTEN)
.......
# lsof -i :21
COMMAND  PID USER   FD   TYPE      DEVICE SIZE NODE NAME
xinetd  9362 root    6u  IPv4 -1253197382       TCP *:ftp (LISTEN)
--------------------------------------------------------

当在lsof后边没有跟任何参数时,该命令将会列出当前系统中被所有进程打开的所有文件
lsof | nl #nl命令打印出行号

下边这几个命令指出打开某文件的进程
lsof `which httpd` #那个进程在使用apache的可执行文件
lsof /etc/passwd #那个进程在占用/etc/passwd
lsof /dev/hda6 #那个进程在占用hda6
lsof /dev/cdrom #那个进程在占用光驱
下边将会打印出占用httpd可执行文件的进程的进程号(仅仅是进程号,在编写shell脚本是有用)
lsof -t `which httpd`

显示出那些文件被以k打头的进程名的进程打开,以bash打头,和以init打头:
lsof -c k
lsof -c bash
lsof -c init

显示出那些文件被以courier打头的进程打开,但是并不属于用户‘zahn’
lsof -c courier -u ^zahn

显示被zahn和apache打开的文件
lsof -u apache,zahn

显示那些文件被pid为3297的进程打开:
lsof +p 3297

显示所有在/tmp文件夹中打开的instance和文件的进程,但是symbol文件并不在列
lsof -D /tmp

显示所有打开的端口
lsof -i

显示所有打开80端口的进程
lsof -i:80

显示所有打开的端口和UNIX domain文件:
lsof -i -U

显示那些进程打开了到www.freeoa.net的UDP的123(ntp)端口的链接:
lsof -iUDP@www.freeoa.net:123

总结一下lsof指令的用法:
lsof freeoa.txt 显示开启文件freeoa.txt的进程
lsof -i :22 知道22端口现在运行什么程序
lsof -c abc 显示abc进程现在打开的文件
lsof -g gid 显示归属gid的进程情况
lsof +d /usr/local/ 显示目录下被进程开启的文件
lsof +D /usr/local/ 同上,但是会搜索目录下的目录,时间较长
lsof -d 4 显示使用fd为4的进程
lsof -i 用以显示符合条件的进程情况


列出在某个端口运行的进程
# lsof -i :port_number

只列出使用 IPv4 的打开文件
# lsof -i 4 - For IPv4

# lsof -i 6 - For ipv6

列出端口在 1-1024 之间的所有进程
# lsof -i :1-1024

根据进程id来列出打开的文件
# lsof -p PID

杀掉某个用户的所有活动进程
# killall -9 `lsof -t -u freeoa`

列出某个目录中被打开的文件
# lsof +D path/directory

根据进程名称列出打开的文件
# lsof -c process_name

列出所有网络连接
# lsof -i

列出所有的TCP、UDP的连接情况
# lsof -i tcp; lsof -i udp;

列出某个用户打开的文件
# lsof -u freeoa

使用'-a'来进行条件组合:属于用户freeoa的且以perlmj打头的进程信息
# lsof -u freeoa -c perlmj -a


lsof可以查看系统打开的文件,这里的“文件”包括/proc文件、磁盘文件、网络IO等,lsof命令常用选项:

常用的参数列表:
lsof filename 显示打开指定文件的所有进程
lsof -a 表示两个参数都必须满足时才显示结果
lsof -c string 显示COMMAND列中包含指定字符的进程所有打开的文件
lsof -u username 显示所属user进程打开的文件
lsof -g gid 显示归属gid的进程情况
lsof +d /DIR/ 显示目录下被进程打开的文件
lsof +D /DIR/ 同上,但是会搜索目录下的所有目录,时间相对较长
lsof -d FD 显示指定文件描述符的进程
lsof -n 不将IP转换为hostname,缺省是不加上-n参数
lsof -i 用以显示符合条件的进程情况
lsof -i[46] [protocol][@hostname|hostaddr][:service|port]
46 –> IPv4 or IPv6
protocol –> TCP or UDP
hostname –> Internet host name
hostaddr –> IPv4地址
service –> /etc/service中的 service name(可以不只一个)
port –> 端口号(可以不只一个)


查看所属freeoa用户进程所打开的文件类型为txt的文件:
# lsof -a -u freeoa -d txt

可以用lsof命令得到结果。
lsof -i:8031,输出:
COMMAND   PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
java    23963 hadoop  197u  IPv4 263056      0t0  TCP htcom:8031 (LISTEN)
java    23963 hadoop  242u  IPv4 263062      0t0  TCP htcom:8031->xd1:45324 (ESTABLISHED)

lsof -i :80
COMMAND PID  USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nginx   347  root   10u  IPv4   6078      0t0  TCP *:http (LISTEN)
nginx   348 nginx   10u  IPv4   6078      0t0  TCP *:http (LISTEN)
nginx   349 nginx   10u  IPv4   6078      0t0  TCP *:http (LISTEN)


FD表示文件描述符,应用程序通过文件描述符来识别文件,文件描述符后有u表示程序是以读写的权限打开文件的(r只读,w只写)。


该文章最后由 Administrator 于 2020-08-13 14:29:49 更新,目前是第 2 版。