【Linux】命令 - lsof

Posted by 西维蜀黍 on 2019-08-01, Last Modified on 2022-12-10

lsof命令是什么

lsof lists on its standard output file information about files opened by processes

被打开的文件可以是

  • regular file
  • directory
  • 网络文件系统的文件
  • 字符设备文件
  • (函数)共享库
  • 管道,命名管道
  • 符号链接
  • a stream or a network file (Internet socket, NFS file or UNIX domain socket.

Columns 含义

  • COMMAND:进程的名称
  • PID:进程标识符
  • PPID:父进程标识符(需要指定-R参数)
  • USER:进程所有者
  • PGID:进程所属组
  • FD:文件描述符,应用程序通过文件描述符识别该文件。如cwd、txt等
    • cwd:表示current work dirctory,即:应用程序的当前工作目录,这是该应用程序启动的目录,除非它本身对这个目录进行更改
    • txt :该类型的文件是程序代码,如应用程序二进制文件本身或共享库,如上列表中显示的 /sbin/init 程序
    • er:FD information error (see NAME column)
    • ltx:shared library text (code and data)
    • mxx :hex memory-mapped type number xx.
    • mem:memory-mapped file
    • mmap:memory-mapped device
    • pd:parent directory
    • rtd:root directory;
    • v86 VP/ix mapped file;
    • 0:表示标准输出
    • 1:表示标准输入
    • 2:表示标准错误

一般在标准输出、标准错误、标准输入后还跟着文件状态模式:r、w、u等

  • u:表示该文件被打开并处于读取/写入模式
  • r:表示该文件被打开并处于只读模式
  • w:表示该文件被打开并处于
  • 空格:表示该文件的状态模式为unknow,且没有锁定
  • -:表示该文件的状态模式为unknow,且被锁定

同时在文件状态模式后面,还跟着相关的锁

  • N:for a Solaris NFS lock of unknown type;

  • r:for read lock on part of the file;

  • R:for a read lock on the entire file;

  • w:for a write lock on part of the file;(文件的部分写锁)

  • W:for a write lock on the entire file;(整个文件的写锁)

  • u:for a read and write lock of any length;

  • U:for a lock of unknown type;

  • x:for an SCO OpenServer Xenix lock on part of the file;

  • X:for an SCO OpenServer Xenix lock on the entire file;

  • space:if there is no lock.

  • TYPE:文件类型,如DIR、REG等,常见的文件类型

    • DIR:表示目录
    • CHR:表示字符类型
    • BLK:块设备类型
    • UNIX: Unix domain socket
    • FIFO:先进先出 (FIFO) 队列
    • IPv4:网际协议 (IP) 套接字
  • DEVICE:指定磁盘的名称

  • SIZE:文件的大小

  • NODE:索引节点(文件在磁盘上的标识)

  • NAME:打开文件的确切名称

Usage

列出打开了某个文件的所有进程

如果不加任何参数,就会打开所有被打开的文件,建议加上特定参数来查看特定信息。

# list all open files
$ lsof
# To find the process that has /u/abe/foo open
$ lsof [/u/abe/foo]

# To list all open files on device /dev/hd4, use:
$ lsof /dev/hd4

# list the processes opening a file via Unix domain socket
$ lsof /tmp/echo.sock
COMMAND     PID   USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
___go_bui 13785 shiwei    3u  unix 0xcc59970df46632e9      0t0      /tmp/echo.sock
___go_bui 13785 shiwei    7u  unix 0xcc59970de99c6161      0t0      /tmp/echo.sock

比如,我用QuickTime播放一个视频,然后列出打开了该视频文件的进程:

$ lsof 584.mp4
COMMAND    PID    USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
QuickTime 9199 wei.shi   12r   REG    1,5  3846660 53157818 584.mp4

-u [user_name] - 根据用户名

# 列出当前用户打开的所有文件
$ lsof -u [username]

# 列出不是用户打开的所有文件
$ lsof -u ^[username]

-c <process_name> - 根据进程名称

  • -c <process_name>: selects the listing of files for processes executing the command that begins with the characters of process_name.
$ ps -ef | grep grafana-s
2061703888   665     1   0  5Mar21 ??         1:52.91 /usr/local/opt/grafana/bin/grafana-server --config /usr/local/etc/grafana/grafana.ini

$ lsof -c grafana-s
COMMAND   PID    USER   FD      TYPE             DEVICE SIZE/OFF                NODE NAME
grafana-s 665 wei.shi  cwd       DIR                1,5      160            30544021 /usr/local/var/lib/grafana
grafana-s 665 wei.shi  txt       REG                1,5 58030576            50725322 /usr/local/Cellar/grafana/7.3.5/bin/grafana-server
grafana-s 665 wei.shi  txt       REG                1,5    28604            60813977 /Library/Preferences/Logging/.plist-cache.USiJWGsl
grafana-s 665 wei.shi  txt       REG                1,5    32768            60603174 /private/var/db/mds/messages/2061703888/se_SecurityMessages
grafana-s 665 wei.shi  txt       REG                1,5  1568368 1152921500312538092 /usr/lib/dyld
grafana-s 665 wei.shi  txt       REG                1,5 28620288 1152921500312405449 /usr/share/icu/icudt64l.dat
grafana-s 665 wei.shi    0r      CHR                3,2      0t0                 310 /dev/null
grafana-s 665 wei.shi    1u      REG                1,5   177523            30544426 /usr/local/var/log/grafana/grafana-stdout.log
...

注意,这里的process_name 采用的是模糊匹配,这意味着如果

$ lsof -c grafan
COMMAND   PID    USER   FD      TYPE             DEVICE SIZE/OFF                NODE NAME
grafana-s 665 wei.shi  cwd       DIR                1,5      160            30544021 /usr/local/var/lib/grafana
grafana-s 665 wei.shi  txt       REG                1,5 58030576            50725322 /usr/local/Cellar/grafana/7.3.5/bin/grafana-server
grafana-s 665 wei.shi  txt       REG                1,5    28604            60813977 /Library/Preferences/Logging/.plist-cache.USiJWGsl
grafana-s 665 wei.shi  txt       REG                1,5    32768            60603174 /private/var/db/mds/messages/2061703888/se_SecurityMessages
grafana-s 665 wei.shi  txt       REG                1,5  1568368 1152921500312538092 /usr/lib/dyld
grafana-s 665 wei.shi  txt       REG                1,5 28620288 1152921500312405449 /usr/share/icu/icudt64l.dat
grafana-s 665 wei.shi    0r      CHR                3,2      0t0                 310 /dev/null
grafana-s 665 wei.shi    1u      REG                1,5   177523            30544426 /usr/local/var/log/grafana/grafana-stdout.log

也是会得到一样的结果。

-p [pid] - 根据进程号

$ lsof -p [pid]

-i - 根据网络连接

# 列出所有的网络连接
$ lsof -i
# Usage
$ lsof -i[46][protocol][@hostname|hostaddr][:service|port]

where:

  • 46 specifies the IP version, IPv4 or IPv6 that applies to the following address. ‘6’ may be be specified only if the UNIX dialect supports IPv6. If neither ‘4’ nor ‘6’ is specified, the following address applies to all IP versions.
  • protocol is a protocol name - TCP, UDP
  • hostname is an Internet host name. Unless a specific IP version is specified, open network files associated with host names of all versions will be selected.
  • hostaddr is a numeric Internet IPv4 address in dot form; or an IPv6 numeric address in colon form, enclosed in brackets, if the UNIX dialect supports IPv6. When an IP version is selected, only its numeric addresses may be specified.
  • service is an /etc/services name - e.g., smtp - or a list of them.
  • port is a port number, or a list of them.

TCP/UDP 相关

# 列出所有 TCP 网络连接信息
$ lsof -i tcp

# 列出所有 UDP 网络连接信息
$ lsof -i udp

$ lsof -n -iTCP
COMMAND     PID  USER   FD   TYPE  DEVICE SIZE/OFF NODE NAME
svnserve  11552 weber    3u  IPv4 3799399      0t0  TCP *:svn (LISTEN)
redis-ser 25501 weber    4u  IPv4  113150      0t0  TCP 127.0.0.1:6379 (LISTEN)

端口相关

# 根据端口列出正在使用该端口的进程
$ lsof -i:[port]

# e.g.,
$ lsof -i:3000
COMMAND   PID    USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
grafana-s 665 wei.shi   14u  IPv6 0x278374e8fe3d2547      0t0  TCP *:hbci (LISTEN)

# 列出使用某个特定的 UDP 端口的进程
$ lsof -iUDP:55

# 列出使用某个特定的 UDP 端口的进程
$ lsof -iTCP:80

IPV4 and IPV6

If -i4 or -i6 is specified with no following address, only files of the indicated IP version, IPv4 or IPv6, are displayed.

# 只显示IPV4
$ lsof -i4

# 只显示IPV6
$ lsof -i6
# Or
$ lsof -i 6

指定 hostname

# Internet IPv4 host address 1.2.3.4
$ lsof -i@1.2.3.4

# Internet IPv6 host address 3ffe:1ebc::1, port 1234
$ lsof -i@[3ffe:1ebc::1]:1234

# list all files using any protocol on ports 513, 514, or 515 of host wonderland.cc.purdue.edu, use:
$ lsof -i @wonderland.cc.purdue.edu:513-515

指定TCP/UDP状态

# Find the process that is listening on a local TCP port:
$ lsof -iTCP -sTCP:LISTEN

# list network files with all UDP states except Idle, use:
$ lsof -iUDP -sUDP:Idle

+D/+d/-d [folder_path] - 根据文件夹路径

  • +d [folder_path]: search for all open instances of inputed directorys and the files and directories it contains at its top level
  • -d [file_number]: specifies a list of file descriptors (FDs) to exclude from or include in the output listing.
  • +D [folder_path]: search for all open instances of inputed directory and all the files and directories it contains to its complete depth.
# 列出打开了该文件夹及该文件夹下文件/文件夹的进程(不递归)
$ lsof +d [folder_path]

# 列出打开了该文件夹及该文件夹下文件/文件夹的进程(递归)
$ lsof +D [folder_path]
$ tree sw_test
sw_test
└── sw_test2
    └── sw_test3

2 directories, 0 files
# 在一个 Shell 进到一个文件夹
$ cd sw_test/sw_test2
# 显示当前 Shell(zsh)的 PID
$ echo $$
10236

# 在另一个 Shell 进到一个文件夹
$ cd sw_test/sw_test2/sw_test3
# 显示当前 Shell(zsh)的 PID
$ echo $$
10393

# 另启动一个 Shell
$ lsof +d sw_test
COMMAND   PID    USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
zsh     10236 wei.shi  cwd    DIR    1,5       64 61411500 sw_test/sw_test2
$ lsof +D sw_test
COMMAND   PID    USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
zsh     10236 wei.shi  cwd    DIR    1,5       96 61411500 sw_test/sw_test2
zsh     10393 wei.shi  cwd    DIR    1,5       64 61413673 sw_test/sw_test2/sw_test3

-U - List all Files used by Unix Domain Socket

$ lsof -U
COMMAND     PID   USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
distnoted   410 shiwei    3u  unix 0xcc59970de99c3ca9      0t0      ->0xcc59970de58b4669
secd        424 shiwei    3u  unix 0xcc59970de99c4091      0t0      ->0xcc59970de58b4669
UserEvent   466 shiwei    3u  unix 0xcc59970de99c5cb1      0t0      ->0xcc59970de99c5d79
UserEvent   466 shiwei    4u  unix 0xcc59970de99c5b21      0t0      ->0xcc59970de58b4669
UserEvent   466 shiwei   17u  unix 0xcc59970de99c3a51      0t0      ->0xcc59970de99c3d71
rapportd    471 shiwei    3u  unix 0xcc59970de99c3731      0t0      ->0xcc59970de58b4669
rapportd    471 shiwei    9u  unix 0xcc59970df4664e41      0t0      ->0xcc59970df4662f01
rapportd    471 shiwei   11u  unix 0xcc59970df4663799      0t0      ->0xcc59970df4664b21
rapportd    471 shiwei   14u  unix 0xcc59970df4663091      0t0      ->0xcc59970df4663159
Sublime     473 shiwei    3u  unix 0xcc59970de99c5351      0t0      /tmp/Sublime Text.4cff18d2bab96a93366319a9e0fa060d.cd247256981dd6a8c8b93b5930202e9d.sock

复合操作

列出某个用户以及某个进程所打开的文件信息

$ lsof -u [user_name] -c [process_name]

Example

$ lsof -p [PID]

如果将一个程序的

一个测试的Python 程序:

import time

print("111")
time.sleep(1)
$ python sw.py
111
...		
$ sudo lsof -p 28902
COMMAND     PID USER   FD   TYPE DEVICE SIZE/OFF   NODE NAME
...
python3.8 28902   pi    0u   CHR  136,0      0t0      3 /dev/pts/0
python3.8 28902   pi    1u   CHR  136,0      0t0      3 /dev/pts/0
python3.8 28902   pi    2u   CHR  136,0      0t0      3 /dev/pts/0

而如果:

$ python sw.py 1> test.log
$ sudo lsof -p 29043
COMMAND     PID USER   FD   TYPE DEVICE SIZE/OFF   NODE NAME
...
python3.8 29043   pi    0u   CHR  136,0      0t0      3 /dev/pts/0
python3.8 29043   pi    1w   REG  179,2        0 422722 /home/pi/Desktop/test.log
python3.8 29043   pi    2u   CHR  136,0      0t0      3 /dev/pts/0

这说明 stdout 被 redict 到了 /home/pi/Desktop/test.log

类似地,

# 把 stdout redict 
$ python sw.py 2> test.log
$ sudo lsof -p 29267
COMMAND     PID USER   FD   TYPE DEVICE SIZE/OFF   NODE NAME
python3.8 29267   pi    0u   CHR  136,0      0t0      3 /dev/pts/0
python3.8 29267   pi    1u   CHR  136,0      0t0      3 /dev/pts/0
python3.8 29267   pi    2w   REG  179,2        0 422722 /home/pi/Desktop/test.log

# 把 stdout 和 stderr 都 redict 
$ python sw.py > test.log 2>&1
$ sudo lsof -p 29333
COMMAND     PID USER   FD   TYPE DEVICE SIZE/OFF   NODE NAME
python3.8 29682   pi    0u   CHR  136,0      0t0      3 /dev/pts/0
python3.8 29682   pi    1w   REG  179,2        0 422722 /home/pi/Desktop/test.log
python3.8 29682   pi    2w   REG  179,2        0 422722 /home/pi/Desktop/test.log

列出某个用户的所有活跃的网络端口

$ lsof -a -u test -i

Reference