字数 5289,阅读大约需 27 分钟
在Rocky Linux运维工作中,你是否遇到过这样的困惑:端口被占用却找不到占用进程?文件删除后磁盘空间仍未释放?程序崩溃提示“文件已打开”却不知道哪个进程在占用?其实,解决这些问题的“万能钥匙”,就是系统自带的命令——lsof。
lsof的全称是“list open files”,直译就是“列出所有打开的文件”。在Linux系统中,“一切皆文件”,无论是普通文件、目录、设备,还是网络套接字、管道,只要被进程打开,lsof都能将其“打回原形”。它就像一个系统侦探,能帮你摸清每一个进程与文件的关联,轻松排查各类运维难题。
本文将以Rocky Linux(与CentOS、RHEL完全兼容)为环境,从入门到精通,用最浅显的语言、最实用的示例,手把手教你掌握lsof,让它成为你运维工具箱中的“得力干将”。
第一部分:入门基础——认识lsof,迈出第一步
1.1 什么是lsof?为什么要用它?
如前文所说,lsof的核心功能是列出系统中所有被进程打开的文件。在Linux中,任何进程运行时,都会打开一些文件(哪怕是隐性的),比如:
- • 程序本身(如/bin/ls)会被打开为“可执行文件”;
- • 进程运行时读取的配置文件(如/etc/profile);
- • 网络连接(如TCP/UDP端口,本质是“网络文件”);
- • 设备文件(如磁盘、键盘、打印机,对应/dev目录下的文件);
当你遇到以下场景时,lsof就能派上大用场:
- • 场景1:启动服务提示“端口已被占用”(如Nginx启动失败,提示80端口被占用);
- • 场景2:删除大文件后,df命令查看磁盘空间仍未释放(大概率是文件被进程占用,删除后仅删除了目录项,进程仍在读取文件内容);
- • 场景3:排查恶意进程(如挖矿进程,通过lsof查看其打开的文件、网络连接,判断其行为);
- • 场景4:查看某个进程正在读取/写入哪些文件(如排查MySQL进程正在操作的日志、数据文件)。
1.2 安装lsof(Rocky Linux默认已安装)
Rocky Linux 8/9系统默认已预装lsof,若未安装(极少情况),执行以下命令安装:
# 安装lsof(root权限)
dnf install -y lsof
# 验证安装是否成功
lsof -v # 查看lsof版本,出现版本信息即为安装成功
注意:lsof需要root权限才能查看所有进程的打开文件,普通用户只能查看自己启动的进程对应的文件。因此,后续示例均建议以root用户执行(或加sudo)。
1.3 最基础的用法:直接输入lsof
直接在终端输入lsof,会列出系统中所有进程打开的所有文件,输出内容非常多(几千行甚至上万行),我们先看一下输出的核心字段含义(重点记,后续所有示例都会用到):
# 直接执行lsof(输出示例,仅截取前几行)
lsof
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
systemd 1 root cwd DIR 8,1 4096 2 /
systemd 1 root rtd DIR 8,1 4096 2 /
systemd 1 root txt REG 8,1 1627008 1310721 /usr/lib/systemd/systemd
systemd 1 root mem REG 8,1 200728 1311045 /usr/lib64/libuuid.so.1.3.0
sshd 986 root txt REG 8,1 823488 1311447 /usr/sbin/sshd
sshd 986 root 3u IPv4 19880 0t0 TCP *:ssh (LISTEN)
核心字段详解(必记):
| | |
|---|
| | |
| | 1(systemd是系统初始化进程,PID永远是1)、986(sshd进程) |
| | root(系统进程)、nginx(Nginx进程) |
| | cwd(当前工作目录)、txt(可执行文件)、mem(内存映射文件)、3u(u表示可读可写) |
| | DIR(目录)、REG(普通文件)、TCP(TCP网络连接)、UDP(UDP网络连接) |
| | 8,1(表示/dev/sda1,8是主设备号,1是次设备号) |
| | 1627008(单位字节)、0t0(网络连接无固定大小,偏移量为0) |
| 文件的inode编号(Linux中文件的唯一标识) | |
| | /(根目录)、*:ssh(监听所有IP的ssh端口,即22端口) |
| | |
- • cwd:current working directory,进程的当前工作目录;
- • txt:text,进程的可执行文件(程序本身);
- • mem:memory-mapped file,内存映射文件(如共享库);
第二部分:核心参数——掌握这些,够用80%场景
直接执行lsof输出太多,实际使用中,我们都会搭配参数过滤需要的信息。lsof的参数非常多,但常用的只有十几个,下面逐个讲解,每个参数都搭配Rocky Linux下的实际示例,确保你能直接复制使用。
2.1 按进程过滤:-p、-c、-u
场景:查看某个进程打开的文件、某个用户的所有进程打开的文件、某个名称的进程打开的文件。
2.1.1 -p:按PID过滤(最常用)
语法:lsof -p 进程ID(可多个,用逗号分隔)
示例1:查看PID为1的systemd进程打开的所有文件(系统初始化进程,打开的文件最多)
lsof -p 1
示例2:查看PID为986(sshd)和1000(nginx)两个进程打开的文件
lsof -p 986,1000
示例3:排除某个PID(加^),查看除了PID为1之外的所有进程打开的文件
lsof -p ^1
2.1.2 -c:按进程名称过滤(模糊匹配)
语法:lsof -c 进程名称(只需要前缀,模糊匹配)
示例1:查看所有名称以“sshd”开头的进程打开的文件(即sshd进程)
lsof -c sshd
示例2:查看所有名称以“ng”开头的进程打开的文件(可能匹配nginx、ngrep等)
lsof -c ng
示例3:排除某个进程名称(加^),查看除了nginx进程之外的所有进程打开的文件
lsof -c ^nginx
2.1.3 -u:按用户过滤(查看某个用户的所有进程打开的文件)
语法:lsof -u 用户名/用户ID
示例1:查看nginx用户的所有进程打开的文件(排查Nginx进程的文件操作)
lsof -u nginx
示例2:查看用户ID为1000的用户(普通用户)的进程打开的文件
lsof -u 1000
示例3:排除某个用户(加^),查看除了root用户之外的所有进程打开的文件
lsof -u ^root
2.2 按文件/目录过滤:+d、+D、直接跟文件名
场景:查看某个文件被哪个进程打开、某个目录下的文件被哪些进程打开(排查文件占用、删除后空间不释放)。
2.2.1 直接跟文件名:查看某个文件被哪个进程打开(最常用)
语法:lsof 文件名(绝对路径或相对路径)
示例1:查看/etc/passwd文件被哪个进程打开(该文件是用户配置文件,通常有多个进程读取)
lsof /etc/passwd
示例2:排查“删除文件后磁盘空间不释放”问题(假设删除了/var/log/nginx/access.log)
# 查看被删除的文件是否仍被进程占用(文件名后会显示(deleted))
lsof /var/log/nginx/access.log
# 若输出结果中NAME字段显示 /var/log/nginx/access.log (deleted),说明该文件被进程占用
# 此时只需杀死对应的PID,即可释放磁盘空间:kill -9 PID
2.2.2 +d:查看某个目录下的文件被哪些进程打开(不递归子目录)
语法:lsof +d 目录路径
示例:查看/var/log目录下的文件被哪些进程打开(不包含子目录,如/var/log/nginx)
lsof +d /var/log
2.2.3 +D:查看某个目录及其子目录下的文件被哪些进程打开(递归,慎用大目录)
语法:lsof +D 目录路径
示例:查看/var/log目录及其所有子目录下的文件被哪些进程打开(包含/var/log/nginx、/var/log/messages等)
lsof +D /var/log
警告:不要对根目录(/)或大目录(如/home)使用+lsof +D,会遍历所有子目录,输出极多,且消耗系统资源。
2.3 按网络连接过滤:-i(最核心、最常用)
场景:查看端口被哪个进程占用、查看某个进程的网络连接、查看TCP/UDP连接状态(排查端口占用、网络异常)。这是lsof最常用的场景之一,务必熟练掌握。
语法:lsof -i [参数],参数可组合(协议、端口、IP、状态)
2.3.1 基础用法:查看所有网络连接(TCP+UDP)
lsof -i
输出中TYPE字段为TCP或UDP,NAME字段显示网络连接信息(如*:ssh表示监听所有IP的22端口,192.168.1.100:ssh->192.168.1.200:54321表示TCP连接)。
2.3.2 按端口过滤(最常用:排查端口占用)
语法:lsof -i :端口号(可多个端口,用逗号分隔;可指定端口范围)
示例1:查看80端口被哪个进程占用(Nginx默认端口,启动失败时常用)
lsof -i :80
示例2:查看80、443、22三个端口被哪些进程占用(常用Web+SSH端口)
lsof -i :80,443,22
示例3:查看1000-2000端口范围内的所有连接
lsof -i :1000-2000
2.3.3 按协议过滤(TCP/UDP)
语法:lsof -i TCP 或 lsof -i UDP
示例1:查看所有TCP连接(如Web服务、SSH连接)
lsof -i TCP
示例2:查看所有UDP连接(如DNS解析、NTP时间同步)
lsof -i UDP
2.3.4 按IP过滤(查看某个IP的网络连接)
语法:lsof -i @IP地址(本地IP或远程IP)
示例1:查看本地IP为192.168.1.100的所有网络连接
lsof -i @192.168.1.100
示例2:查看远程IP为192.168.1.200的所有网络连接(即本机与192.168.1.200的连接)
lsof -i @192.168.1.200
2.3.5 组合过滤(协议+端口+IP,实战常用)
示例1:查看TCP协议、80端口的所有连接(Nginx的HTTP服务)
lsof -i TCP:80
示例2:查看本地IP为192.168.1.100、TCP协议、443端口的连接(Nginx的HTTPS服务)
lsof -i TCP@192.168.1.100:443
示例3:查看远程IP为192.168.1.200、TCP协议、22端口的连接(本机与远程的SSH连接)
lsof -i TCP@192.168.1.200:22
2.3.6 查看监听状态的网络连接(-i -sTCP:LISTEN)
场景:查看本机正在监听的端口(即对外提供服务的端口),类似netstat -tuln。
# 查看所有监听状态的TCP连接(常用)
lsof -i -sTCP:LISTEN
# 查看所有监听状态的UDP连接
lsof -i -sUDP:LISTEN
# 查看80端口的监听状态(确认Nginx是否正常监听)
lsof -i :80 -sTCP:LISTEN
说明:-s参数用于指定协议状态,TCP的常见状态有LISTEN(监听)、ESTABLISHED(已建立连接)、TIME_WAIT(等待关闭)等。
2.4 其他常用参数
2.4.1 -t:只输出PID(方便批量操作)
语法:lsof -t [其他参数],只输出进程ID,不输出其他字段,常用于批量杀死进程。
示例1:查看占用80端口的进程PID,然后批量杀死
# 查看占用80端口的PID
lsof -t -i :80
# 杀死占用80端口的所有进程(慎用,确认是无用进程再执行)
lsof -t -i :80 | xargs kill -9
示例2:查看nginx用户的所有进程PID,批量重启(假设nginx进程重启命令为systemctl restart nginx,此处仅示例批量杀死)
lsof -t -u nginx | xargs kill -9
2.4.2 -a:逻辑与(AND),多条件过滤
语法:lsof -a [条件1] [条件2],表示同时满足两个条件的结果(默认是逻辑或OR)。
示例1:查看root用户且PID为986的进程打开的文件(同时满足USER=root和PID=986)
lsof -a -u root -p 986
示例2:查看TCP协议且处于ESTABLISHED状态(已建立连接)的连接,且属于sshd进程
lsof -a -c sshd -i TCP -sTCP:ESTABLISHED
2.4.3 -n:不解析主机名(加速输出)
语法:lsof -n [其他参数],查看网络连接时,不将IP解析为主机名,可加快命令执行速度(尤其是网络环境差的情况下)。
# 查看80端口连接,不解析主机名(NAME字段显示IP,不显示主机名)
lsof -n -i :80
2.4.4 -P:不解析端口名(加速输出)
语法:lsof -P [其他参数],查看网络连接时,不将端口号解析为端口名称(如22解析为ssh,80解析为http),加速输出。
# 查看80端口连接,不解析端口名(NAME字段显示:80,不显示:http)
lsof -P -i :80
常用组合:lsof -nP -i :80(不解析主机名和端口名,最快查看80端口占用)。
第三部分:实战场景——手把手解决运维常见问题
掌握了上面的参数,我们结合Rocky Linux运维中最常见的5个场景,实战演练,确保你能学以致用。
场景1:Nginx启动失败,提示“80端口已被占用”(最常见)
问题现象:执行systemctl start nginx,提示“Job for nginx.service failed because the control process exited with error code.”,查看日志(journalctl -u nginx)显示“bind() to 0.0.0.0:80 failed (98: Address already in use)”。
解决步骤:
# 步骤1:查看80端口被哪个进程占用(核心命令)
lsof -nP -i :80
# 假设输出结果如下(PID为1234,进程名为httpd,即Apache进程)
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
httpd 1234 root 4u IPv4 23456 0t0 TCP *:80 (LISTEN)
# 步骤2:杀死占用80端口的进程(若httpd是无用进程)
kill -9 1234
# 步骤3:再次启动Nginx,即可成功
systemctl start nginx
补充:若占用端口的是有用进程(如httpd需要保留),则修改Nginx的监听端口(编辑/etc/nginx/nginx.conf,将listen 80改为listen 8080),重启Nginx即可。
场景2:删除大日志文件后,磁盘空间仍未释放
问题现象:执行rm -rf /var/log/nginx/access.log(该文件大小为100G),然后执行df -h查看,发现/分区空间仍为100%,未释放。
原因:该日志文件被Nginx进程占用,删除后仅删除了目录项(文件名),但进程仍在读取文件内容,文件的inode节点未被释放,因此磁盘空间不释放。
解决步骤:
# 步骤1:查看被删除的文件是否仍被进程占用(核心命令)
lsof /var/log/nginx/access.log
# 假设输出结果如下(NAME字段显示(deleted),说明文件已删除但被进程占用)
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 1567 nginx 5w REG 8,1 107374182400 1234 /var/log/nginx/access.log (deleted)
# 步骤2:杀死对应的进程(或重启进程,推荐重启,避免影响服务)
# 方法1:杀死单个PID(不推荐,可能有多个nginx进程)
kill -9 1567
# 方法2:重启Nginx进程(推荐,优雅释放文件,不影响服务连续性)
systemctl restart nginx
# 步骤3:再次查看磁盘空间,已释放
df -h
补充:避免该问题的方法:不要直接rm删除正在被进程占用的日志文件,而是使用echo "" > /var/log/nginx/access.log(清空文件内容,不删除文件,进程仍可正常写入)。
场景3:查看MySQL进程正在操作哪些文件(排查数据读写异常)
问题现象:MySQL进程占用CPU过高,怀疑是某个数据文件或日志文件读写异常,需要查看MySQL进程正在打开哪些文件。
解决步骤:
# 步骤1:查看MySQL进程的PID(MySQL进程名为mysqld)
lsof -t -c mysqld # 输出PID,假设为2000
# 步骤2:查看该PID打开的所有文件(重点关注TYPE为REG的普通文件,即数据/日志文件)
lsof -p 2000
# 重点关注NAME字段,常见的MySQL文件如下:
# /var/lib/mysql/xxx.frm(表结构文件)
# /var/lib/mysql/xxx.ibd(表数据文件,InnoDB引擎)
# /var/log/mysqld.log(MySQL日志文件)
# 步骤3:过滤出MySQL进程打开的日志文件(精准排查)
lsof -a -p 2000 +d /var/log/mysqld
场景4:排查恶意进程(如挖矿进程)
问题现象:服务器CPU占用100%,top命令查看发现有一个未知进程(如minerd)占用大量CPU,需要查看该进程的行为(打开的文件、网络连接),判断是否为恶意进程。
解决步骤:
# 步骤1:查看未知进程的PID(假设进程名为minerd)
lsof -t -c minerd # 输出PID,假设为3000
# 步骤2:查看该进程打开的所有文件(判断是否读取恶意配置文件、写入挖矿日志)
lsof -p 3000
# 步骤3:查看该进程的网络连接(判断是否连接外部挖矿服务器)
lsof -nP -a -p 3000 -i
# 若输出结果中NAME字段显示连接外部IP(如192.168.1.300:8888),且该IP是挖矿服务器,则可确认是恶意进程
# 步骤4:杀死恶意进程,并删除对应的可执行文件(根据lsof输出的txt字段找到文件路径)
kill -9 3000
rm -rf /tmp/minerd # 假设txt字段显示该进程的可执行文件路径为/tmp/minerd
场景5:查看本机与远程服务器的SSH连接(排查远程登录异常)
问题现象:怀疑有非法IP远程登录本机SSH(22端口),需要查看当前所有SSH连接的远程IP。
解决步骤:
# 方法1:查看所有SSH连接(进程名为sshd,TCP协议,ESTABLISHED状态)
lsof -nP -a -c sshd -i TCP -sTCP:ESTABLISHED
# 输出结果示例(NAME字段显示本地IP:ssh->远程IP:端口,即远程IP为192.168.1.200)
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
sshd 4000 root 4u IPv4 56789 0t0 TCP 192.168.1.100:22->192.168.1.200:54321 (ESTABLISHED)
# 方法2:只查看远程IP(过滤NAME字段,提取远程IP)
lsof -nP -a -c sshd -i TCP -sTCP:ESTABLISHED | awk '{print $9}' | awk -F'->' '{print $2}' | awk -F':' '{print $1}'
# 若发现未知远程IP(如192.168.1.500),可关闭该连接(杀死对应的PID)
kill -9 4000
第四部分:进阶技巧——让lsof更高效
4.1 组合参数快速定位问题
实战中,我们通常会组合多个参数,快速过滤需要的信息,以下是几个常用的组合命令,可直接复制使用:
# 1. 快速查看80端口占用(不解析主机名/端口名,最快)
lsof -nP -i :80
# 2. 查看所有监听端口(类似netstat -tuln)
lsof -nP -i -sTCP:LISTEN
# 3. 查看nginx进程的所有网络连接
lsof -nP -a -c nginx -i
# 4. 查看被删除但仍被进程占用的文件(排查磁盘空间不释放)
lsof | grep deleted
# 5. 查看root用户的所有TCP连接
lsof -nP -a -u root -i TCP
4.2 输出内容保存到文件,便于分析
当lsof输出内容过多时,可将输出保存到文件,后续慢慢分析:
# 将80端口的所有连接信息保存到lsof_80.log文件中
lsof -nP -i :80 > /tmp/lsof_80.log
# 查看文件内容
cat /tmp/lsof_80.log
4.3 结合其他命令使用(如grep、awk)
lsof的输出可通过grep、awk过滤,提取更精准的信息:
# 1. 查看所有ESTABLISHED状态的TCP连接(过滤状态)
lsof -nP -i TCP | grep ESTABLISHED
# 2. 提取所有网络连接的远程IP(去重)
lsof -nP -i | awk '{print $9}' | awk -F'->' '{print $2}' | awk -F':' '{print $1}' | sort -u
# 3. 查看nginx进程打开的所有.log文件(过滤日志文件)
lsof -a -c nginx | grep .log
第五部分:总结——lsof核心用法回顾
lsof是Rocky Linux运维中不可或缺的工具,核心功能是“列出所有打开的文件”,本质是“进程与文件的关联探测器”。掌握以下核心点,就能解决80%的运维场景:
- 1. 核心场景:端口占用、文件占用(磁盘空间不释放)、进程行为排查、网络连接异常;
- 2. 最常用参数:-i(网络连接)、-p(PID)、-c(进程名)、-u(用户)、+d/+D(目录)、-t(只输出PID);
- • 查看端口占用:lsof -nP -i :端口号
- • 排查磁盘不释放:lsof | grep deleted
- • 批量杀进程:lsof -t [条件] | xargs kill -9
其实lsof的参数还有很多,但以上内容已经足够应对日常运维工作。建议大家多动手练习,结合实际问题使用,慢慢就能熟练掌握这个“系统侦探”的用法,让你的运维工作更高效、更轻松!