引言
在《Linux fuser工具介紹》一文中,與大家一起學習了fuser工具的使用方法。"lsof"——list open files,lsof也是Linux下用於查看打開的文件以及相關聯進程信息的工具,相比fuser提供更強大的功能,下面我們就來學習lsof的使用方法。
lsof輸出說明
直接執行lsof,將輸出系統中所有打開文件的信息,這里說的“文件”不單指一般的文件,還包括目錄、socket套接字、設備文件和命名管道:
#lsof COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME init 1 root cwd DIR 8,2 4096 2 / init 1 root rtd DIR 8,2 4096 2 / init 1 root txt REG 8,2 40784 6488145 /sbin/init init 1 root mem REG 8,2 19114 3874831 /lib64/libdl-2.11.1.so init 1 root 10u FIFO 0,15 0t0 2677 /dev/initctl ……
每行顯示一個打開的文件,各列含義如下:
- COMMAND:與文件關聯進程的名稱
- PID:進程PID
- USER:拉起進程的用戶
- FD:指示進程與文件的關聯關系
- TYPE:指示文件類型
- DEVICE:指示文件所在設備的設備號(major,minor)
- SIZE/OFF:指示文件的大小或進程對文件操作的偏移量
- NODE:文件索引標識
- NAME:文件名
FD列值的含義與fuser查詢結果中PID后接字符的含義相同,指示了進程與文件的關聯關系,有以下常見取值:
- cwd:進程工作目錄
- txt:進程由該文件拉起(如二進制文件或腳本)
- rtd:進程的根目錄
- mem:內存映射文件
- N(u/w/r):指示該文件為進程打開的第N個文件描述符,u為可讀可寫模式,w為可寫模式,r為可讀模式
TYPE列有以下常見取值:
- REG:一般文件
- DIR:目錄
- CHR:字符設備
- BLK:塊設備
- FIFO:命名管道
- PIPE:管道
- IPV4:ipv4套接字
- unix:unix域套接字
根據以上字段,我們就可以了解到哪個文件以何種關聯方式與哪個進程關聯。但單執行lsof輸出太多,為獲取必要輸出,我們就得加一些過濾選項。
查詢某個進程打開的文件
lsof常見的用法是查找進程打開的文件名稱和文件數目,以下我們使用lsof查看pid為14586的syslog-ng進程使用了哪些文件:
# lsof -p 14586 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME syslog-ng 14586 root cwd DIR 8,2 4096 2 / syslog-ng 14586 root rtd DIR 8,2 4096 2 / syslog-ng 14586 root txt REG 8,2 386456 6488333 /sbin/syslog-ng syslog-ng 14586 root mem REG 8,2 61467 3874842 /lib64/libnss_files-2.11.1.so syslog-ng 14586 root 0r CHR 1,3 0t0 2707 /dev/null syslog-ng 14586 root 1w CHR 1,3 0t0 2707 /dev/null syslog-ng 14586 root 2w CHR 1,3 0t0 2707 /dev/null syslog-ng 14586 root 3u unix 0xffff8804787d59c0 0t0 23639 /var/lib/dhcp/dev/log syslog-ng 14586 root 4u unix 0xffff880475db46c0 0t0 23641 /dev/log syslog-ng 14586 root 5w REG 8,2 2268859 5858016 /var/log/messages
從以上查詢結果可知,syslog-ng進程由/sbin/syslog-ng拉起,進程根目錄和當前工作目錄均為系統根目錄,進程的標准輸出、標准輸入和標准出錯輸出均被重定向至/dev/null,/var/log/messages被syslog-ng以可寫模式打開。
Linux限制了進程能夠打開文件的數目,通過如下命令可以查看該限制:
# ulimit -n 1024
若進程打開文件句柄的數目超過系統限制,則相應進程不能正常服務,使用如下方式我們可以查看各個進程打開文件句柄的數目:
# lsof -n|awk '{print $2}'|sort|uniq -c|sort -nr| more
查詢某個文件被哪些進程打開
lsof命令后直接接文件名,可以查到該文件被哪些進程打開:
# lsof /var/log/messages COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME syslog-ng 14586 root 5w REG 8,2 2326134 5858016 /var/log/messages
要刪除文件時要先中止進程,而不是直接刪除這個文件。
在umount文件系統時,如果文件系統中有打開的文件,那么umount操作會失敗,報“device is busy”。這時可使用”lsof /dev/sdaX”顯示sdaX文件系統中被打開的所有文件,再關閉所列文件。
進程的當前工作目錄影響文件系統的卸載,這也是為什么在編寫后台進程時需要將其工作目錄設置為根目錄的原因,
恢復刪除文件
最后,來介紹使用lsof命令恢復被刪文件的用法:某文件被刪除,但從lsof能查到該文件仍被某進程打開,這時我們可以恢復被刪文件。以下通過一個小程序說明該用法:
#include <unistd.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main(int argc, char** argv) { int fd; char buf[10]; fd = open("test.txt", O_RDONLY); read(fd, buf, 10); while(1) { sleep(2); printf("%s", buf); } return 0; }
以上程序打開test.txt文件並定時輸出該文件內容,在編譯、拉起該程序之后,我們將test.txt文件刪除,之后使用lsof查到打開該文件的進程:
# lsof | grep test.txt ttt 12347 root 3r REG 8,2 6 2334724 /tmp/lx/test.txt (deleted)
接着,在/proc中可以看到該進程打開的文件描述符如下:
# ll /proc/12347/fd total 0 lrwx------ 1 root root 64 May 2 11:43 0 -> /dev/pts/1 lrwx------ 1 root root 64 May 2 11:43 1 -> /dev/pts/1 lrwx------ 1 root root 64 May 2 11:43 2 -> /dev/pts/1 lr-x------ 1 root root 64 May 2 11:43 3 -> /tmp/lx/test.txt (deleted)
執行以下命令我們即可將/tmp/lx/test.txt文件的內容恢復至/tmp/test.txt:
# cat /proc/12347/fd/3 > /tmp/test.txt
當進程打開了某個文件時,只要該進程保持打開該文件,即使將文件刪除,文件依然存在於磁盤中。進程並不知道文件已經被刪除,它依然可以使用打開該文件時獲取到的文件描述符進行讀取和寫入。除了該進程外,這個文件是不可見的,因為已經刪除了其相應的目錄條目。
小結
本文介紹了lsof的使用方法,包括lsof命令的輸出格式,如何根據某進程查詢其打開的文件,又如何查詢某文件被哪些進程打開,以及判斷進程打開文件數是否達到上限的方法、刪除文件/卸載文件系統時需注意的事項,最后介紹了使用lsof恢復被刪除文件的方法。