AI Infra 训练营
总览
  • Day 1 · 集群起步 + CNI
  • Day 2 · 控制面 + etcd
  • Day 3 · CRD + Operator + Webhook
  • Day 4 · 存储深度
  • Day 5 · 卷扩容 + 安全
  • Day 6 · 调度 + 可观测
  • Day 7 · Harbor + ArgoCD + Mesh
  • Day 8 · AI Infra
  • Day 9 · Triton + GPU
  • Day 10 · MIG + HPA + 量化
  • Day 11 · AI Agent 端到端
  • Day 12 · 灾备
  • Day 13 · Operator + 联邦 + Mesh + RAG
  • Day 14 · CKA / CKS + 总结
  • LLM 训练手册
  • RAG + Agent 手册
  • 推理优化手册
  • 上下文工程手册
  • Agent 开发手册
  • 面试深度复盘
  • 训练 v2 深度手册
  • 心智模型
  • 看懂命令输出
  • 容器网络底层
  • K8s 网络深入
  • DNS 全套
  • 故障排查方法论
  • 心智模型
  • 容器挂载完整指南
  • K8s Volumes 大全
  • PV/PVC/CSI 深入
  • NFS 深入
  • 分布式存储概览
  • 故障排查 runbook
HiHuo 主站
GitHub
总览
  • Day 1 · 集群起步 + CNI
  • Day 2 · 控制面 + etcd
  • Day 3 · CRD + Operator + Webhook
  • Day 4 · 存储深度
  • Day 5 · 卷扩容 + 安全
  • Day 6 · 调度 + 可观测
  • Day 7 · Harbor + ArgoCD + Mesh
  • Day 8 · AI Infra
  • Day 9 · Triton + GPU
  • Day 10 · MIG + HPA + 量化
  • Day 11 · AI Agent 端到端
  • Day 12 · 灾备
  • Day 13 · Operator + 联邦 + Mesh + RAG
  • Day 14 · CKA / CKS + 总结
  • LLM 训练手册
  • RAG + Agent 手册
  • 推理优化手册
  • 上下文工程手册
  • Agent 开发手册
  • 面试深度复盘
  • 训练 v2 深度手册
  • 心智模型
  • 看懂命令输出
  • 容器网络底层
  • K8s 网络深入
  • DNS 全套
  • 故障排查方法论
  • 心智模型
  • 容器挂载完整指南
  • K8s Volumes 大全
  • PV/PVC/CSI 深入
  • NFS 深入
  • 分布式存储概览
  • 故障排查 runbook
HiHuo 主站
GitHub

lsof —— 列出打开的文件(list open files)

一句话定义

lsof 列出进程打开的文件——包括普通文件、目录、设备、网络 socket、unix socket、pipe、设备等。在 Linux 里"一切皆文件",所以 lsof 能回答各种"谁在用什么"的问题。

典型场景

  • "为啥端口被占了":lsof -i :8080
  • "rm 文件但磁盘没释放":lsof | grep deleted
  • "为啥这个 unmount 不掉":lsof <mount-point>
  • "containerd.sock 谁在连":lsof /run/containerd/containerd.sock
  • "进程打开了多少 fd":lsof -p <PID> + wc -l

装

apt install -y lsof              # Debian / Ubuntu
yum install -y lsof              # CentOS / RHEL

大多数系统自带。


输出格式

COMMAND   PID USER   FD   TYPE     DEVICE  SIZE/OFF   NODE NAME
sshd     1234 root  cwd    DIR        8,3      4096      2 /
sshd     1234 root  rtd    DIR        8,3      4096      2 /
sshd     1234 root  txt    REG        8,3    855648 123456 /usr/sbin/sshd
sshd     1234 root  mem    REG        8,3    166400 123457 /lib/x86_64-linux-gnu/libc.so.6
sshd     1234 root    0u   CHR        1,3       0t0      6 /dev/null
sshd     1234 root    3u  IPv4     123456      0t0    TCP *:22 (LISTEN)
sshd     1234 root    4u  IPv6     123457      0t0    TCP *:22 (LISTEN)
列含义
COMMAND进程名
PID进程 ID
USER拥有者
FD文件描述符(数字 + 模式 / 特殊标记)
TYPE文件类型
DEVICE主设备号 / 次设备号
SIZE/OFF大小或偏移量
NODEinode
NAME文件名 / socket 描述

FD 列的特殊值

值含义
cwdcurrent working directory
rtdroot directory(chroot 后)
txttext(可执行文件本身)
mem内存映射文件
0u / 1u / 2ustdin / stdout / stderr(u = read/write)
3r / 3w / 3u数字 + r (read) / w (write) / u (both)

TYPE 列

值含义
REG普通文件
DIR目录
CHR字符设备
BLK块设备
FIFO命名管道
IPv4 / IPv6网络 socket
unixunix domain socket

核心 5 个用法

1. lsof -i :<port> —— 谁占了某端口

lsof -i :8080
# COMMAND  PID  USER   FD   TYPE  DEVICE SIZE/OFF NODE NAME
# nginx   1234  root    6u  IPv4  123456    0t0    TCP *:8080 (LISTEN)

也可以指定协议:

lsof -i tcp:8080
lsof -i udp:53
lsof -i 4 -i tcp                       # 仅 IPv4 TCP
lsof -i @10.0.24.28                    # 跟某 IP 相关的连接
lsof -i @10.0.24.28:6443                # 跟某 IP:port 相关

2. lsof <path> —— 谁打开了某文件

lsof /var/log/syslog
# COMMAND  PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
# rsyslogd 567 root 8w REG  8,3    1234567   ... /var/log/syslog

3. lsof <directory> —— 谁在用某目录

lsof +D /mnt/data
# +D 递归显示该目录下的所有
# 慢但全面

排查 umount: device is busy 必备。

4. lsof -p <PID> —— 某进程打开的所有文件

lsof -p 1234 | head
lsof -p 1234 | wc -l                   # 这进程打开了多少 fd

5. lsof -u <user> —— 某用户打开的

lsof -u root
lsof -u nginx -i                       # nginx 用户的网络连接

实战场景

场景 1:端口被占用

$ systemctl start nginx
Job for nginx.service failed because the control process exited with error.
# ... bind: Address already in use

$ lsof -i :80
# COMMAND  PID  USER FD TYPE DEVICE SIZE/OFF NODE NAME
# httpd   2345  root 4u IPv4 234567    0t0   TCP *:80 (LISTEN)

# 是 Apache 在占!
systemctl stop apache2
systemctl start nginx

ss -lntp 是更现代的等价物,但 lsof -i 在某些场景更直观(含进程名)。

场景 2:rm 文件后磁盘没释放

df -h /
# /dev/sda3   95% full

rm /var/log/huge.log
df -h /
# /dev/sda3   95% full         ← 还是满的!

lsof | grep deleted
# rsyslogd  567  root  8w  REG  8,3  10737418240  123456 /var/log/huge.log (deleted)

某进程持有这个文件的句柄 → Linux 不真释放空间。

修:

systemctl restart rsyslog        # 重启那个进程,句柄关 → 空间释放

# 或者优雅一点(不重启)让进程关那个 fd:不容易,多数情况只能 restart

K8s 节点上 journald / kubelet / containerd 是常见占用者。

场景 3:umount 卡住

$ umount /mnt/data
umount: /mnt/data: target is busy.

$ lsof +D /mnt/data | head
# COMMAND  PID  USER FD TYPE DEVICE SIZE/OFF NODE NAME
# bash    3456  root cwd DIR   8,16   4096    1   /mnt/data
# vim     4567  root  3u REG   8,16   1234    2   /mnt/data/notes.txt

找到占用者,kill 或者让它退出,再 umount。

或者懒卸载:

umount -l /mnt/data              # lazy 立刻断开 mount,但保留旧 fd 工作直到关闭

场景 4:进程打开太多 fd

lsof -p 1234 | wc -l
# 65000           ← 接近 ulimit 上限

# 看大概是什么
lsof -p 1234 | awk '{print $5}' | sort | uniq -c | sort -rn | head
# 60000 IPv4              ← 大部分是网络连接
#   200 REG
#   ...

fd 泄漏。看应用代码。

场景 5:找跟 unix socket 相关的进程

# 谁在连 containerd
lsof /run/containerd/containerd.sock
# COMMAND  PID    USER  FD TYPE  DEVICE SIZE/OFF NODE NAME
# kubelet  1234   root  5u unix 0x...  0t0      ... /run/containerd/containerd.sock

# 谁在连 docker
lsof /var/run/docker.sock

排查"kubelet 连不上 containerd" / "kubectl exec 卡"。

场景 6:看 K8s pod 内进程开的文件

# 找 pod 容器的 PID
PID=$(crictl inspect $(crictl ps -q --name my-pod) | jq '.info.pid')

# 看它开了什么
lsof -p $PID

# 看它的网络连接
lsof -p $PID -i

# 看是不是 fd 泄漏
lsof -p $PID | wc -l

注意:在节点 root 上跑 lsof 看 pod 进程的 fd 是看不到 pod 内 mount 视角的路径(namespace 隔离)—— 你看到的是宿主机的路径。

要 pod 视角:

nsenter -t $PID --mount --net lsof -p $PID

看网络连接

lsof -i                                # 所有网络连接
lsof -i tcp                            # 只 TCP
lsof -i -P -n                          # -P 不解析端口名,-n 不解析 IP
lsof -i :22                            # 22 端口
lsof -i tcp:80-443                     # 端口范围
lsof -i @gateway.local                 # 跟某主机相关
lsof -i 4 -i :443                      # IPv4 + 443

-P -n 标配:不加的话 lsof 会反向 DNS + 解析端口名,慢。

lsof -i -P -n | grep ESTABLISHED
# 所有已建立的网络连接

lsof vs ss 在网络场景的对比

维度sslsof -i
速度快(netlink)慢(遍历所有进程)
信息网络专用,无进程文件综合
看哪个进程-p自带
TCP 状态过滤state establishedgrep
推荐日常优先用 sslsof 在排错复杂场景
ss -lntp                               # 看监听 + 进程,简洁快
lsof -i :8080                          # 看 8080 端口被谁占(同等效果)

看进程的工作目录 / 已执行二进制

lsof -p 1234 -a -d cwd                 # cwd
# COMMAND   PID  USER  FD TYPE DEVICE SIZE/OFF NODE NAME
# python   1234  root  cwd DIR  8,3   4096    1234 /opt/myapp

lsof -p 1234 -a -d txt                 # 可执行文件(text 段)
# python   1234  root  txt REG  8,3   3000000 5678 /usr/bin/python3.10

-a 表示多个过滤条件 AND(默认是 OR)。 -d cwd 限定 fd 类型。

或者更直接:

readlink /proc/1234/cwd
readlink /proc/1234/exe
ls -l /proc/1234/fd/

训练营常用速查

# 谁占了 ApiServer 端口
lsof -i :6443

# 谁连 etcd
lsof -i :2379

# pod 内进程开的文件
lsof -p $(crictl inspect $(crictl ps -q --name my-pod) | jq '.info.pid')

# 找 deleted 文件释放磁盘
lsof | grep deleted | sort -k7 -rn | head

# 看 kubelet 与 containerd 的 unix socket
lsof /var/run/containerd/containerd.sock
lsof /var/run/cri.sock

# 排查节点出网慢:哪些进程在大量网络连接
lsof -i -P -n | awk '{print $1}' | sort | uniq -c | sort -rn | head

常见踩坑

坑 1:lsof 跑得超慢

lsof                                   # 几秒到几十秒

lsof 默认遍历所有进程的所有 fd——大节点几万个 fd。

加 flag 缩小范围:

lsof -p <PID>                          # 限定 PID
lsof -i :8080                          # 限定 socket
lsof -nP                               # 跳过 DNS / 服务名解析
lsof -c <command>                      # 按进程名

特别 -n -P 大幅提速。

坑 2:root 才能看全

$ lsof -i :22
# 没输出 / 只显示自己的进程

sudo lsof -i :22。看别人进程必须 root。

坑 3:lsof +D 慢得吓人

lsof +D /                              # 跑半小时

+D 递归一整个文件系统。只在小目录用:

lsof /mnt/data                         # 仅匹配这一层
lsof +D /mnt/data                      # 递归(慢)

坑 4:在容器里 lsof 看不到完整路径

$ kubectl exec my-pod -- lsof | head
# 路径是 pod 内的视角(/app/...)

容器有自己的 mount namespace,路径意义和宿主机不同。这是预期行为。

坑 5:alpine 容器没装 lsof

$ kubectl exec my-pod -- lsof
sh: lsof: not found

alpine 默认没 lsof。装:apk add lsof。

或者用 /proc:

ls -l /proc/<PID>/fd/                  # 等价 lsof -p

坑 6:FD 列出现奇怪的字母

FD
NOFD             ← 无法读取 fd 信息(权限不够)
cwd
DEL              ← 已删除的内存映射
ERR              ← 读 fd 失败

NOFD 通常是没权限;DEL 是 mmap 的文件已被删除,但仍在内存里映射。

坑 7:lsof 没显示 "deleted" 但 du / df 差很多

du -sh /var               # 5 GB
df /var                   # used 15 GB

差 10 GB。lsof 应该能找到 deleted 文件,但有些类型的"被持有的空间"lsof 看不到:

  • tmpfs 中的内存(restart 才释放)
  • 容器 overlay 层
  • 旧 kernel 中的 mmap

通用排查:

lsof | grep deleted
ls -la /proc/*/fd/* 2>/dev/null | grep deleted

坑 8:-i 和 -c 混用语义

lsof -i :80 -c nginx                   # 默认 OR
lsof -a -i :80 -c nginx                # AND

要"既符合 -i 又符合 -c" 必须加 -a。多数 lsof 选项默认 OR,反直觉。


关联命令

  • ss —— 网络场景比 lsof 快
  • ps —— 找 PID
  • strace —— 看进程实时 syscall
  • fuser —— 简化版 lsof(只显示 PID):fuser -v /mnt/data
  • du -sh + lsof | grep deleted —— 排查"磁盘满"组合拳
  • crictl —— 找 pod 容器的 PID
  • /proc/<PID>/fd/ —— lsof 看不到时用这个
在 GitHub 上编辑此页