lsof (List Open Files) 貫徹 Unix「一切皆檔案」哲學:普通檔、目錄、NFS、區塊/字元裝置、共享庫、管道、Socket、inode 皆可查詢。本文系統整理核心參數、輸出欄位含義、進程/用戶/目錄/網路四大查詢維度、實戰排查場景 (端口佔用、刪除檔案空間未釋放、NFS 掛載點忙碌)、輸出格式化與腳本整合技巧。
一、核心參數速查表
| 參數 | 功能 | 範例 |
lsof /path/to/file | 顯示開啟指定檔案的所有進程 | lsof /var/log/messages |
-c string | COMMAND 欄包含字串的進程 | lsof -c nginx |
-p PID | 指定進程 ID (可 -p ^123 排除) | lsof -p 1234 |
-u user | 指定用戶 (可 -u ^root 排除) | lsof -u www |
-g GID | 指定群組 ID | lsof -g 1000 |
+d /DIR | 目錄下被開啟的檔案 (非遞迴) | lsof +d /var/log |
+D /DIR | 遞迴查詢目錄樹 (較慢) | lsof +D /home |
-a | AND 邏輯組合多條件 | lsof -a -c nginx -u www |
-N | 列出 NFS 檔案 | lsof -N |
-d FD | 指定檔案描述符 (數字、範圍 0-2、特殊值) | lsof -d 0-2 |
-n | 不解析 IP→主機名 (加速) | lsof -n -i |
-i | 網路連線過濾 (最強大) | lsof -i:22 |
二、-i 參數完整語法 (網路過濾神器)
lsof -i[46][protocol][@hostname|hostaddr][:service|port]
| 元素 | 說明 | 範例 |
4/6 | IPv4 / IPv6 | -i4, -i6 |
protocol | TCP / UDP | -iTCP, -iUDP |
@hostname | 遠端主機名 | -i@google.com |
@hostaddr | 遠端 IP | -i@192.168.1.1 |
:service | 服務名 (/etc/services) | -i:ssh, -i:http |
:port | 埠號 (可多個逗號分隔) | -i:80,443,8080 |
組合範例
lsof -iTCP:22 # 本機監聽/連線 SSH
lsof -iTCP@192.168.1.100:80 # 連往特定 IP:80 的 TCP 連線
lsof -iUDP:53 # DNS 查詢連線
lsof -i:8080 -a -c java # Java 進程佔用 8080
三、輸出欄位詳細解讀
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
sshd 1390 root 3u IPv4 13050 0t0 TCP *:ssh (LISTEN)
sshd 1390 root 4u IPv6 13056 0t0 TCP *:ssh (LISTEN)
sshd 36454 root 3r IPv4 94352 0t0 TCP host:ssh->172.16.0.1:50018 (ESTABLISHED)
| 欄位 | 含義 |
COMMAND | 進程名稱 (前 15 字元) |
PID | 進程 ID |
USER | 進程擁有者 |
FD | 檔案描述符:cwd(工作目錄) txt(程式碼/庫) mem(記憶體映射) mmap 0u(標準輸入) 1u(輸出) 2u(錯誤) 3r(讀) 4w(寫) 5u(讀寫) |
TYPE | 檔案類型:REG(普通) DIR(目錄) CHR(字元裝置) BLK(區塊) FIFO(管道) SOCK(Socket) IPv4/IPv6 |
DEVICE | 裝置號 (major,minor) |
SIZE/OFF | 檔案大小/位移 |
NODE | inode 號 |
NAME | 檔案路徑/網路端點 |
四、實戰排查場景
1. 端口被佔用,找不到進程
# 查看 8080 埠
lsof -i:8080
# 若無輸出但 netstat 顯示 LISTEN,可能是權限不足
sudo lsof -i:8080
# 或進程在不同網路命名空間 (Docker)
nsenter -t $(docker inspect -f '{{.State.Pid}}' container) -n lsof -i:8080
2. 刪除大檔案後磁碟空間未釋放
# 尋找已刪除但仍被開啟的檔案 (TYPE=REG, NAME 標記 deleted)
lsof -nP | grep deleted
# 輸出範例:
# java 1234 user 12u REG 253,0 1073741824 12 /tmp/huge.log (deleted)
# 解決:重啟進程或透過 /proc/1234/fd/12 截斷
: > /proc/1234/fd/12
3. NFS 掛載點無法卸載 (device busy)
lsof -N /mnt/nfs_share
# 找出佔用進程,kill 後再 umount
4. 追蹤特定進程所有網路連線
lsof -p 1234 -i -a
# -a 將 -p 與 -i 邏輯 AND
5. 統計用戶開啟檔案數 (防止超過 ulimit -n)
lsof -u www | wc -l
# 或按進程統計
lsof -u www | awk '{print $2}' | sort | uniq -c | sort -nr
五、特殊 FD 值對照表 (-d 參數)
| 值 | 含義 |
mem | 記憶體映射檔案 (mmap) |
mmap | 記憶體映射裝置 |
txt | 載入記憶體執行的程式碼段 (text segment) |
cwd | 當前工作目錄 |
rtd | 根目錄 |
0/1/2 | 標準輸入/輸出/錯誤 |
六、輸出格式化與腳本整合
# 機器可解析格式 (欄位固定寬度)
lsof -F pcfn # P=PID, c=COMMAND, f=FD, n=NAME
# 輸出範例:
# p1234
# cnginx
# f3
# n/var/log/nginx/access.log
# 腳本範例:找出佔用 80 埠的 PID 並重啟
PID=$(lsof -ti:80)
if [ -n "$PID" ]; then
kill -9 $PID
systemctl restart nginx
fi
七、效能與權限提示
lsof 掃描 /proc,進程數萬時較慢,加 -n 避免 DNS 解析可大幅加速
- 非 root 執行僅能看到自身進程,完整視圖需
sudo
- 核心執行緒 (kthreadd 等) 無 FD,不會出現在輸出中