歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Unix知識 >> Unix基礎知識

使用lsof查找打開的文件

簡介:通過查看打開的文件,了解更多關於系統的信息。了解應用程序打開了哪些文件或者哪個應用 程序打開了特定的文件,作為系統管理員,這將使得您能夠作出更好的決策。例如,您不應該卸載具有打 開文件的文件系統。使用 lsof,您可以檢查打開的文件,並根據需要在卸載之前中止相應的進程。同樣 地,如果您發現了一個未知的文件,那麼可以找出到底是哪個應用程序打開了這個文件。

在 UNIX® 環境中,文件無處不在,這便產生了一句格言:“任何事物都是文件”。通過文件 不僅僅可以訪問常規數據,通常還可以訪問網絡連接和硬件。在有些情況下,當您使用 ls 請求目錄清單 時,將出現相應的條目。在其他情況下,如傳輸控制協議 (TCP) 和用戶數據報協議 (UDP) 套接字,不存 在相應的目錄清單。但是在後台為該應用程序分配了一個文件描述符,無論這個文件的本質如何,該文件 描述符為應用程序與基礎操作系統之間的交互提供了通用接口。

因為應用程序打開文件的描述符 列表提供了大量關於這個應用程序本身的信息,所以能夠查看這個列表將是很有幫助的。完成這項任務的 實用程序稱為 lsof,它對應於“list open files”(列出打開的文件)。幾乎在每個 UNIX 版本中都有這個實用程序,但奇怪的是,大多數供應商並沒有將其包含在操作系統的初始安裝中。要獲取 更多關於 lsof 的信息,請參見參考資料部分。

lsof 簡介

只需輸入 lsof 就可以生成大 量的信息,如清單 1 所示。因為 lsof 需要訪問核心內存和各種文件,所以必須以 root 用戶的身份運 行它才能夠充分地發揮其功能。

清單 1. lsof 的示例輸出

bash-3.00# lsof
COMMAND  PID  USER  FD  TYPE    DEVICE SIZE/OFF   NODE NAME
sched     0  root cwd  VDIR     136,8   1024     2 /
init     1  root cwd  VDIR     136,8   1024     2 /
init     1  root txt  VREG     136,8   49016   1655 /sbin/init
init     1  root txt  VREG     136,8  51084    3185 /lib/libuutil.so.1
vi    2013  root  3u VREG     136,8    0    8501 /var/tmp/ExXDaO7d
...

每行顯示一個打開的文件,除非另外指定,否 則將顯示所有進程打開的所有文件。Command、PID 和 User 列分別表示進程的名稱、進程標識符 (PID) 和所有者名稱。Device、SIZE/OFF、Node 和 Name 列涉及到文件本身的信息,分別表示指定磁盤的名稱 、文件的大小、索引節點(文件在磁盤上的標識)和該文件的確切名稱。根據 UNIX 版本的不同,可能將 文件的大小報告為應用程序在文件中進行讀取的當前位置(偏移量)。清單 1 來自一台可以報告該信息 的 Sun Solaris 10 計算機,而 Linux® 沒有這個功能。

FD 和 Type 列的含義最為模糊,它 們提供了關於文件如何使用的更多信息。FD 列表示文件描述符,應用程序通過文件描述符識別該文件。 Type 列提供了關於文件格式的更多描述。我們來具體研究一下文件描述符列,清單 1 中出現了三種不同 的值。cwd 值表示應用程序的當前工作目錄,這是該應用程序啟動的目錄,除非它本身對這個目錄進行更 改。txt 類型的文件是程序代碼,如應用程序二進制文件本身或共享庫,再比如本示例的列表中顯示的 init 程序。最後,數值表示應用程序的文件描述符,這是打開該文件時返回的一個整數。在清單 1 輸出 的最後一行中,您可以看到用戶正在使用 vi 編輯 /var/tmp/ExXDaO7d,其文件描述符為 3。u 表示該文 件被打開並處於讀取/寫入模式,而不是只讀 (r) 或只寫 (w) 模式。有一點不是很重要但卻很有幫助, 初始打開每個應用程序時,都具有三個文件描述符,從 0 到 2,分別表示標准輸入、輸出和錯誤流。正 因為如此,大多數應用程序所打開的文件的 FD 都是從 3 開始。

與 FD 列相比,Type 列則比較 直觀。根據具體操作系統的不同,您會發現將文件和目錄稱為 REG 和 DIR(在 Solaris 中,稱為 VREG 和 VDIR)。其他可能的取值為 CHR 和 BLK,分別表示字符和塊設備;或者 UNIX、FIFO 和 IPv4,分別 表示 UNIX 域套接字、先進先出 (FIFO) 隊列和網際協議 (IP) 套接字。

轉到 /proc 目錄

盡管與使用 lsof 沒有什麼直接的關系,但對 /proc 目錄進行簡要的介紹是有必要的。/proc 是 一個目錄,其中包含了反映內核和進程樹的各種文件。這些文件和目錄並不存在於磁盤中,因此當您對這 些文件進行讀取和寫入時,實際上是在從操作系統本身獲取相關信息。大多數與 lsof 相關的信息都存儲 於以進程的 PID 命名的目錄中,所以 /proc/1234 中包含的是 PID 為 1234 的進程的信息。

在 /proc 目錄的每個進程目錄中存在著各種文件,它們可以使得應用程序簡單地了解進程的內存空間、文件 描述符列表、指向磁盤上的文件的符號鏈接和其他系統信息。lsof 實用程序使用該信息和其他關於內核 內部狀態的信息來產生其輸出。稍後我將把 lsof 的輸出與 /proc 目錄中的信息聯系起來。

常見 用法

前面,我向您介紹了如何簡單地運行不帶任何參數的 lsof,以便顯示關於每個進程所打開的 文件的信息。本文余下的部分將重點關注如何使用 lsof 來顯示所需的信息以及如何正確地對其進行解釋 。

查找應用程序打開的文件

lsof 常見的用法是查找應用程序打開的文件的名稱和數目。 您可能想嘗試找出某個特定應用程序將日志數據記錄到何處,或者正在跟蹤某個問題。例如,UNIX 限制 了進程能夠打開文件的數目。通常這個數值很大,所以不會產生問題,並且在需要時,應用程序可以請求 更大的值(直到某個上限)。如果您懷疑應用程序耗盡了文件描述符,那麼可以使用 lsof 統計打開的文 件數目,以進行驗證。

要指定單個進程,可以使用 -p 參數,後面加上該進程的 PID。因為這樣 做不僅會返回該應用程序所打開的文件,還會返回共享庫和代碼,所以通常需要對輸出進行篩選。要完成 此任務,可以使用 -d 標志根據 FD 列進行篩選,使用 -a 標志表示兩個參數都必須滿足 (AND)。如果沒 有 -a 標志,缺省的情況是顯示匹配任何一個參數 (OR) 的文件。清單 2 顯示了 sendmail 進程打開的 文件,並使用 txt 對這些文件進行篩選。

清單 2. 帶有 PID 篩選器並進行 txt 文件描述符篩選 的 lsof 輸出

sh-3.00# lsof -a -p 605 -d ^txt
COMMAND PID USER  FD  TYPE  DEVICE SIZE/OFF   NODE NAME
sendmail 605 root cwd  VDIR 136,8   1024  23554 /var/spool/mqueue
sendmail 605 root  0r VCHR 13,2      6815752 /devices/pseudo/mm@0:null
sendmail 605 root  1w VCHR 13,2      6815752 /devices/pseudo/mm@0:null
sendmail 605 root  2w VCHR 13,2      6815752 /devices/pseudo/mm@0:null
sendmail 605 root  3r DOOR       0t0    58
    /var/run/name_service_door(door to nscd[81]) (FA:->0x30002b156c0)
sendmail 605 root  4w VCHR 21,0      11010052
             /devices/pseudo/log@0:conslog->LOG
sendmail 605 root  5u IPv4 0x300010ea640    0t0   TCP *:smtp (LISTEN)
sendmail 605 root  6u IPv6 0x3000431c180   0t0    TCP *:smtp (LISTEN)
sendmail 605 root  7u IPv4 0x300046d39c0   0t0   TCP *:submission (LISTEN)
sendmail 605 root  8wW VREG     281,3    32 8778600 /var/run/sendmail.pid

清單 2 為 lsof 指定了三個參數。第一個是 -a,它表示當所有 的參數都為真時,才顯示這個文件。第二個參數是 -p 605,它限制僅輸出 PID 為 605 的進程,可以通 過 ps 命令獲取這個信息。最後一個參數 -d ^txt,它表示篩選出其中 txt 類型的記錄(脫字符號 [^] 表示排除)。

清單 2 的輸出提供了關於進程行為的信息。如 cwd 行所示,該應用程序的工作目 錄為 /var/spool/mqueue。文件描述符 0、1 和 2 分配給了 /dev/null(Solaris 大量使用符號鏈接, 所以這裡顯示了相應的偽設備)。FD 3 是一個 Solaris 門(高速遠程過程調用 (RPC) 接口),以只讀 模式打開。FD 4 中的內容比較有趣,因為它是一個字符設備的只讀句柄,實質上是 /dev/log。從這個文 件中,您可以收集該應用程序向 UNIX syslog 守護進程進行的記錄,所以 /etc/syslog.conf 規定了日 志文件的位置。

作為一個網絡應用程序,sendmail 對網絡端口進行監聽。文件描述符 5、6 和 7 可以告訴您,該應用程序正以 IPv4 和 IPv6 模式監聽簡單郵件傳輸協議 (SMTP) 端口,並以 IPv4 模式 監聽提交端口。最後一個文件描述符是只寫的,並且指向 /var/run/sendmail.pid。FD 列中的大寫 W 表 示該應用程序具有對整個文件的寫鎖。該文件用於確保每次只能打開一個應用程序實例。

查找打 開某個文件的應用程序

在其他情況下,您有一個文件或目錄,並且需要知道哪個應用程序控制了 該文件(打開了該文件)。清單 2 顯示了由 sendmail 進程打開了 /var/run/sendmail.pid。如果您不 知道這個信息,那麼在給定文件名的情況下,lsof 可以提供該信息。清單 3 顯示了相應的輸出。

清單 3. 要求 lsof 顯示關於某個文件的信息

bash-3.00# lsof /var/run/sendmail.pid
COMMAND PID USER  FD  TYPE DEVICE SIZE/OFF  NODE NAME
sendmail 605 root  8wW VREG 281,3    32 8778600 /var/run/sendmail.pid

正如輸出所示,進程 sendmail(PID 為 605)控制了文件 /var/run/sendmail.pid,並且通過排它鎖打 開該文件以便進行寫入。如果出於某種原因,您需要刪除這個文件,那麼正確的做法是中止該進程,而不 是直接刪除這個文件。否則,這個守護進程下次可能無法正常啟動,或者可能稍後會啟動另一個實例,從 而導致爭用。

有時您只知道在文件系統的某處打開了文件。在卸載文件系統時,如果該文件系統 中有任何打開的文件,那麼操作將會失敗。通過指定裝入點的名稱,您可以使用 lsof 顯示一個文件系統 中所有打開的文件。清單 4 顯示了如何嘗試卸載 /export/home,然後使用 lsof 找出誰在使用該文件系 統。

清單 4. 使用 lsof 找出誰在使用文件系統

bash-3.00# umount /export/home
umount: /export/home busy
bash-3.00# lsof /export/home
COMMAND  PID USER  FD  TYPE DEVICE SIZE/OFF NODE NAME
bash  1943 root cwd  VDIR 136,7   1024  4 /export/home/sean
bash  2970 sean cwd  VDIR 136,7   1024  4 /export/home/sean
ct   3030 sean cwd  VDIR 136,7   1024  4 /export/home/sean
ct   3030 sean  1w VREG 136,7    0  25 /export/home/sean/output

在這個示例中,用戶 sean 正在其 home 目錄中進行一些操作 。有兩個 bash(一種 Shell)實例正在運行,並且當前目錄設置為 sean 的 home 目錄。還有一個名為 ct 的應用程序正運行於相同的目錄,並且其標准輸出(文件描述符 1)重定向到一個名為 output 的文 件。要成功地卸載 /export/home,應該在通知用戶以確保情況正常之後,中止這些進程。

這個示 例說明了應用程序的當前工作目錄非常重要,因為它仍保持著文件資源,並且可以防止文件系統被卸載。 這就是為什麼大部分守護進程(後台蹋┙塹哪柯幾奈柯肌⒒蚍裉囟ǖ哪柯跡ㄈ?sendmail 示例中的 /var/spool/mqueue)的原因,以避免該守護進程阻止卸載不相關的文件系統。如果 sendmail 從 /export/home/sean 目錄啟動,並且沒有將其目錄更改為 /var/spool/mqueue,那麼在卸載 /export/home 前必須中止它。

如果您對非裝入點目錄中打開的文件感興趣,那麼必須通過 +d 或 +D 指定該目錄的名稱,具體使用其中的哪一個標志取決於您需要遞歸到子目錄(+D)或者不需要遞歸到 子目錄(+d)。例如,要查看 /export/home/sean 中所有打開的文件,可以使用 lsof +D /export/home/sean。在前面的示例中,相關的目錄是一個裝入點,而這裡與前面的示例存在細微的差別 ,並且限制了 lsof 和內核之間的交互。這還會引起潛在的問題,即 lsof /export/home 與 lsof /export/home/(請注意尾部的斜槓)有所區別。第一種方式可以正常工作,因為它指向了裝入點。第二 種方式不會生成任何輸出,因為它指向了目錄。如果您在 Shell 中使用 Tab 鍵自動完成命令,那麼可能 碰到這個問題,其中會幫助您添加結尾的斜槓。在這種情況下,您可以刪除這個斜槓或者使用 +D 指定目 錄。前者是首選的方法,因為與指定任意的目錄相比,其執行速度更快。

不常見的用法

在 前面的部分中,我們研究了 lsof 的基本用法,即顯示打開的文件和控制它們的進程之間的關系。當您想 對系統進行一些煩瑣的操作,而又不希望破壞別人重要的文檔時,這種方法很有幫助。您還可以使用相同 的方法執行一些高難度的 UNIX 操作。

恢復刪除的文件

當 UNIX 計算機受到入侵時,常見 的情況是日志文件被刪除,以掩蓋攻擊者的蹤跡。管理錯誤也可能導致意外刪除重要的文件,比如在清理 舊日志時,意外地刪除了數據庫的活動事務日志。有時可以恢復這些文件,並且 lsof 可以為您提供幫助 。

當進程打開了某個文件時,只要該進程保持打開該文件,即使將其刪除,它依然存在於磁盤中 。這意味著,進程並不知道文件已經被刪除,它仍然可以向打開該文件時提供給它的文件描述符進行讀取 和寫入。除了該進程之外,這個文件是不可見的,因為已經刪除了其相應的目錄條目。

前面曾在 轉到 /proc 目錄部分中說過,通過在適當的目錄中進行查找,您可以訪問進程的文件描述符。在隨後的 內容中,您看到了 lsof 可以顯示進程的文件描述符和相關的文件名。您能明白我的意思嗎?

但 願它真的這麼簡單!當您向 lsof 傳遞文件名時,比如在 lsof /file/I/deleted 中,它首先使用 stat () 系統調用獲得有關該文件的信息,不幸的是,這個文件已經被刪除。在不同的操作系統中,lsof 可能 可以從核心內存中捕獲該文件的名稱。清單 5 顯示了一個 Linux 系統,其中意外地刪除了 Apache 日志 ,我正使用 grep 工具查找是否有人打開了該文件。

清單 5. 在 Linux 中使用 lsof 查找刪除的 文件

# lsof | grep error_log
httpd   2452   root  2w   REG    33,2   499  3090660
          /var/log/httpd/error_log (deleted)
httpd   2452   root  7w   REG    33,2   499  3090660
           /var/log/httpd/error_log (deleted)
... more httpd processes ...

在這 個示例中,您可以看到 PID 2452 打開文件的文件描述符為 2(標准錯誤)和 7。因此,可以在 /proc/2452/fd/7 中查看相應的信息,如清單 6 所示。

清單 6. 通過 /proc 查找刪除的文件

# cat /proc/2452/fd/7
[Sun Apr 30 04:02:48 2006] [notice] Digest: generating secret for digest authentication
[Sun Apr 30 04:02:48 2006] [notice] Digest: done
[Sun Apr 30 04:02:48 2006] [notice] LDAP: Built with OpenLDAP LDAP SDK

Linux 的優點在於,它保存了文件的名稱,甚至可以告訴我們它已經被刪除。在遭到 破壞的系統中查找相關內容時,這是非常有用的內容,因為攻擊者通常會刪除日志以隱藏他們的蹤跡。 Solaris 並不提供這些信息。然而,我們知道 httpd 守護進程使用了 error_log 文件,所以可以使用 ps 命令找到這個 PID,然後可以查看這個守護進程打開的所有文件。

清單 7. 在 Solaris 中查 找刪除的文件

# lsof -a -p 8663 -d ^txt
COMMAND PID  USER  FD  TYPE     DEVICE SIZE/OFF  NODE NAME
httpd  8663 nobody cwd  VDIR     136,8   1024     2 /
httpd  8663 nobody  0r VCHR     13,2     6815752 /devices/pseudo/mm@0:null
httpd  8663 nobody  1w VCHR     13,2      6815752 /devices/pseudo/mm@0:null
httpd  8663 nobody  2w VREG     136,8    185 145465 / (/dev/dsk/c0t0d0s0)
httpd  8663 nobody  4r DOOR           0t0   58 /var/run/name_service_door
            (door to nscd[81]) (FA: ->0x30002b156c0)
httpd  8663 nobody  15w VREG     136,8   185 145465 / (/dev/dsk/c0t0d0s0)
httpd  8663 nobody  16u IPv4 0x300046d27c0   0t0   TCP *:80 (LISTEN)
httpd  8663 nobody  17w VREG     136,8    0 145466
                              /var/apache/logs/access_log
httpd  8663 nobody  18w VREG     281,3    0 9518013 /var/run (swap)

我使用 -a 和 -d 參數對輸出進行篩選,以排除代碼程序段,因為我知道需要查找的是哪些文件。Name 列顯示出 ,其中的兩個文件(FD 2 和 15)使用磁盤名代替了文件名,並且它們的類型為 VREG(常規文件)。在 Solaris 中,刪除的文件將顯示文件所在的磁盤的名稱。通過這個線索,就可以知道該 FD 指向一個刪除 的文件。實際上,查看 /proc/8663/fd/15 就可以得到所要查找的數據。

如果可以通過文件描述 符查看相應的數據,那麼您就可以使用 I/O 重定向將其復制到文件中,如 cat /proc/8663/fd/15 > /tmp/error_log 。此時,您可以中止該守護進程(這將刪除 FD,從而刪除相應的文件),將這個臨時文 件復制到所需的位置,然後重新啟動該守護進程。

對於許多應用程序,尤其是日志文件和數據庫 ,這種恢復刪除文件的方法非常有用。正如您所看到的,有些操作系統(以及不同版本的 lsof)比其他 的系統更容易查找相應的數據。

查找網絡連接

網絡連接也是文件,這意味著可以使用 lsof 獲得關於它們的信息。您曾在清單 2 中看到過這樣的示例。該示例假設您已經知道 PID,但是有時 候並非如此。如果您只知道相應的端口,那麼可以使用 -i 參數利用套接字信息進行搜索。清單 8 顯示 了對 TCP 端口 25 的搜索。

清單 8. 查找監聽端口 25 的進程

# lsof -i :25
COMMAND PID USER  FD  TYPE    DEVICE SIZE/OFF NODE NAME
sendmail 605 root   5u IPv4 0x300010ea640   0t0 TCP *:smtp (LISTEN)
sendmail 605 root  6u IPv6 0x3000431c180   0t0 TCP *:smtp (LISTEN)

需要以 protocol:@ip:port 的形式向 lsof 實用程序傳遞相關信息,其中的 protocol 為 TCP 或 UDP(可以使用 4 或 6 作為前綴,表示 IP 的版本),IP 為可解析的名稱或 IP 地址,而 port 為數字或表示該服務的名稱(來自 /etc/services )。需要一個或多個元素(端口、IP、協議)。在清單 8 中,:25 表示端口 25。輸出顯示,進程 605 正在使用 IPv6 和 IPv4 監聽端口 25。如果您對 IPv4 不感興趣,那麼可以將篩選器改為 6:25,以表示 監聽端口 25 的 IPv6 套接字,或者直接使用 6 表示所有的 IPv6 連接。

除了顯示出這些守護進 程正在監聽的對象,lsof 還可以發現發生的連接,同樣是使用 -i 參數。清單 9 顯示了搜索與 192.168.1.10 之間的所有連接。

清單 9. 搜索活動的連接

# lsof -i @192.168.1.10
COMMAND PID USER  FD  TYPE    DEVICE SIZE/OFF NODE NAME
sshd   1934 root  6u IPv6 0x300046d21c0 0t1303608 TCP sun:ssh->linux:40379
                (ESTABLISHED)
sshd  1937 root  4u IPv6 0x300046d21c0 0t1303608 TCP sun:ssh->linux:40379
               (ESTABLISHED)

在這個示例中,sun 和 linux 之間有兩個 IPv6 連接。對其進行更仔細的研究可以看出 ,這些連接來自於兩個不同的進程,但它們卻是相同的,這是因為兩台主機是相同的,並且端口也是相同 的(ssh 和 40379)。這是由於進入主進程的連接分叉出一個處理程序,並將該套接字傳遞給它。您還可 以看到,名為 sun 的計算機正在使用端口 22 (ssh),而 linux 具有端口 40379。這表示,sun 是該連 接的接收者,因為它關聯於該服務的已知端口。40379 是源或臨時端口,並且僅對這個連接有意義。

因為,至少在 UNIX 中,套接字是另一類文件,所以 lsof 可以獲得關於這些連接的詳細信息, 並找出誰對它們負責。

結束語

UNIX 大量使用了文件。作為系統管理員,lsof 允許您對核 心內存進行查看,以找出系統當前如何使用這些文件。lsof 最簡單的用法可以告訴您哪些進程打開了哪 些文件,以及哪些文件由哪些進程打開。在收集關於應用程序工作情況的信息時,或在進行某些可能損壞 數據的操作前確保文件未被使用時,這一點特別重要lsof 更高級的用法可以幫助您查找刪除的文件,並 獲得關於網絡連接的信息。這是一個功能強大的工具,它幾乎可以用於任何地方。

Copyright © Linux教程網 All Rights Reserved