lsof(list open files)是一个列出当前系统打开文件的工具。在linux环境下,任何事物都以文件的形式存在,通过文件不仅仅可以访问常规数据,还可以访问网络连接和硬件。所以如传输控制协议(TCP)和用户数据报协议(UDP)套接字等,系统在后台都为该应用程序分配了一个文件描述符,无论这个文件的本质如何,该文件描述符为应用程序与基础操作系统之间的交互提供了通用接口。因为应用程序打开文件的描述符列表提供了大量关于这个应用程序本身的信息,因此通过lsof工具能够查看这个列表对系统监测以及排错将是很有帮助的。 ## 安装 lsof 命令可能没有安装,Redhat安装方法: ``` yum install lsof ``` ## 列出所有打开的文件 不带任何参数执行 `lsof` 命令会输出当前所有活跃进程打开的所有文件: ```bash [yz@localhost ~]$ lsof | less COMMAND PID TID USER FD TYPE DEVICE SIZE/OFF NODE NAME systemd 1 root cwd unknown /proc/1/cwd (readlink: Permission denied) systemd 1 root rtd unknown /proc/1/root (readlink: Permission denied) systemd 1 root txt unknown /proc/1/exe (readlink: Permission denied) systemd 1 root NOFD /proc/1/fd (opendir: Permission denied) kthreadd 2 root cwd unknown /proc/2/cwd (readlink: Permission denied) kthreadd 2 root rtd unknown /proc/2/root (readlink: Permission denied) ``` 其中列 `COMMAND` 、`PID`、`USER` 分别表示进程名、进程ID、所属用户。 列 `FD` 是文件描述符,下面是可能的类型以及说明 | FD | 说明 | | :--: | :----------: | | cwd | 当前目录 | | txt | txt文件 | | rtd | root目录 | | mem | 内存映射文件 | 列 `TYPE` 是文件类型,下面是可能的值以及说明 | TYPE | 说明 | | :-----: | :----------------: | | DIR | 目录 | | REG | 普通文件 | | CHR | 字符 | | a_inode | Inode文件 | | FIFO | 管道或者socket文件 | | netlink | 网络 | | unknown | 未知 | 列 `DEVICE` 表示设备ID 列 `SIZE/OFF` 表示进程大小 列 `NODE` 表示文件的Inode号 列`NAME` 表示路径或者链接 ## 列出指定用户已打开的文件 使用 `-u` 选项可以列出指定用户已经打开的文件,该选项后面可以接多个用户名,每个用户名之间用空格隔开,表示列出所有指定用户已打开的所有文件。 ```bash [yz@localhost ~]$ lsof -u yz COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME sshd 2805 yz cwd unknown /proc/2805/cwd (readlink: Permission denied) sshd 2805 yz rtd unknown /proc/2805/root (readlink: Permission denied) sshd 2805 yz txt unknown /proc/2805/exe (readlink: Permission denied) sshd 2805 yz NOFD /proc/2805/fd (opendir: Permission denied) ``` 如果要排除指定用户已经打开的文件,可以在用户名前加 `^` 符号,下面的命令会列出除yz用户外其他所有用户已打开了的文件。 ``` lsof -u ^yz ``` ## 找出打开着但已被删除了的文件 ``` [yz@localhost ~]$ lsof -u yz | grep deleted vim 27813 yz 4u REG 253,1 12288 131167 /home/yz/.p.txt.swp(deleted) ``` ## 列出所有打开了的网络文件 ```bash [yz@localhost ~]$ lsof -i COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME node 10444 yz 27u IPv4 1356954 0t0 TCP localhost:45393->localhost:36542 (ESTABLISHED) postgres 17442 yz 5u IPv6 824828 0t0 TCP localhost:7404 (LISTEN) postgres 17442 yz 6u IPv4 824829 0t0 TCP localhost:7404 (LISTEN) postgres 17442 yz 8u IPv6 824834 0t0 UDP localhost:37288->localhost:37288 ``` * 列出所有 IPV4/6 网络文件 列出所有已经打开了的 ipv4 网络文件: ```bash [yz@localhost ~]$ lsof -i 4 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME node 10444 yz 27u IPv4 1356954 0t0 TCP localhost:45393->localhost:36542 (ESTABLISHED) postgres 17442 yz 6u IPv4 824829 0t0 TCP localhost:7404 (LISTEN) node 24258 yz 18u IPv4 848159 0t0 TCP localhost:45393 (LISTEN) node 24258 yz 21u IPv4 1353624 0t0 TCP localhost:45393->localhost:36540 (ESTABLISHED) node 24258 yz 23u IPv4 1353553 0t0 TCP localhost:45393->localhost:36532 (ESTABLISHED) node 24352 yz 23u IPv4 1353556 0t0 TCP localhost:45393->localhost:36534 (ESTABLISHED) ``` 所有已经打开了的 ipv6 网络文件: ```bash [yz@localhost ~]$ lsof -i 6 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME postgres 17442 yz 5u IPv6 824828 0t0 TCP localhost:7404 (LISTEN) postgres 17442 yz 8u IPv6 824834 0t0 UDP localhost:37288->localhost:37288 postgres 17444 yz 8u IPv6 824834 0t0 UDP localhost:37288->localhost:37288 postgres 17445 yz 8u IPv6 824834 0t0 UDP localhost:37288->localhost:37288 ``` * 列出在指定端口上打开的文件 使用 `lsof -i:端口号` 可以获得所有在指定端口号上打开的文件。 ```bash [yz@localhost ~]$ lsof -i:7404 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME postgres 17442 yz 5u IPv6 824828 0t0 TCP localhost:7404 (LISTEN) postgres 17442 yz 6u IPv4 824829 0t0 TCP localhost:7404 (LISTEN) ``` 上面例子列出了所有在7404号端口上打开的文件。 在服务器开发中,经常会部署一个网关或者代理程序,用来和客户端通讯,网关或者代理程序需要开放一个固定的端口供客户端连接用。 如果客户端连接不上网关或者代理程序,我们可以用上述命令检查网关或代理程序的端口是否开启,来排除因为端口关闭了导致连接不上网关的情况。 * 列出使用了指定协议(TCP/UDP) 的文件 使用 `lsof -i TCP/UDP` 列出使用了TCP 或 UDP 协议的文件。 ```bash [yz@localhost ~]$ lsof -i TCP COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME node 10444 yz 27u IPv4 1356954 0t0 TCP localhost:45393->localhost:36542 (ESTABLISHED) postgres 17442 yz 5u IPv6 824828 0t0 TCP localhost:7404 (LISTEN) postgres 17442 yz 6u IPv4 824829 0t0 TCP localhost:7404 (LISTEN) node 24258 yz 18u IPv4 848159 0t0 TCP localhost:45393 (LISTEN) node 24258 yz 21u IPv4 1353624 0t0 TCP localhost:45393->localhost:36540 (ESTABLISHED) node 24258 yz 23u IPv4 1353553 0t0 TCP localhost:45393->localhost:36532 (ESTABLISHED) node 24352 yz 23u IPv4 1353556 0t0 TCP localhost:45393->localhost:36534 (ESTABLISHED) ``` 使用 `lsof -i TCP:7404` 列出使用了TCP 协议并且端口为7404的文件。 使用 `lsof -i TCP:1-7404` 列出使用了TCP协议并且端口范围为 1 到 7404 的文件。 ```bash [yz@localhost ~]$ lsof -i TCP:1-7404 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME postgres 17442 yz 5u IPv6 824828 0t0 TCP localhost:7404 (LISTEN) postgres 17442 yz 6u IPv4 824829 0t0 TCP localhost:7404 (LISTEN) ``` ## 列出目录中所有打开的文件 可以使用`lsof`命令列出指定目录中的所有打开文件。 现有一个`data`目录 ,结构如下: ```bash [yz@localhost ~]$ tree ~/test /home/yz/test └── git_demo └── Readme.txt 1 directory, 1 file ``` 列出 `data` 目录中打开的文件: ```bash [yz@localhost ~]$ lsof +D ~/test COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME vim 29235 yz 4u REG 253,2 12288 274648855 /home/yz/test/git_demo/.Readme.txt.swp [yz@localhost ~]$ lsof +d ~/test ``` 上面例子中,`+D` 和 `+d` 选项都是列出目录中打开的文件。 `+D` 选项会列出一个目录和其子目录中打开的文件,而 `+d` 选项只会列出当前目录下已打开的文件。 ## 列出指定进程ID打开的文件 进程ID是操作系统进程的唯一标识,以下命令列出了进程ID为 17442 相关的文件, 从结果中可以知道这个进程ID对应的进程是PostgreSQL。 ```bash [yz@localhost ~]$ ps ux | grep postgresql yz 1612 0.0 0.0 112728 996 pts/2 S+ 15:39 0:00 grep --color=auto postgresql yz 17442 0.0 0.1 272944 13308 ? Ss 01:16 0:01 /home/yz/postgresql/pg_bin/bin/postgres -D /home/yz/postgresql/pg_data [yz@localhost ~]$ lsof -p 17442 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME postgres 17442 yz cwd DIR 253,2 4096 268882575 /home/yz/postgresql/pg_data postgres 17442 yz rtd DIR 253,0 244 64 / postgres 17442 yz txt REG 253,2 51299336 135847173 /home/yz/postgresql/pg_bin/bin/postgres postgres 17442 yz mem REG 253,0 61624 33644885 /usr/lib64/libnss_files-2.17.so …… ``` 上述命令中,`-p` 选项后面可以指定多个进程ID,每个进程ID之间用逗号分隔,如果想排除掉某个进程打开的文件,可以在该进程ID前面加上 `^`符号。 ``` lsof -p 1,2,3,^4 ``` 上述命令会列出进程1,进程2,进程3打开的所有文件,同时忽略进程4打开的文件。 ## 杀死指定用户的所有进程 前面介绍了列出指定用户所有打开的文件,我们可以组合 `kill` 命令一起使用,实现杀死指定用户的所有进程的功能,具体的命令如下: ``` kill -9 `lsof -t -u yz` ``` 上述命令中,`lsof -u yz` 是列出`yz`用户所有打开的文件,加上 `-t` 选项之后表示结果只列出PID列,也就是进程ID列,其他列都忽略,前面的 `kill -9` 表示强制结束指定的进程ID。 ## 小结 本文介绍了 `lsof` 命令的一些常见用法,它还有很多其他的用法,请自行查看man文档。