使用 lsof 查詢開啟的檔案(轉)

amyz發表於2007-08-10
使用 lsof 查詢開啟的檔案(轉)[@more@]

  透過檢視開啟的檔案,瞭解更多關於系統的資訊。瞭解應用程式開啟了哪些檔案或者哪個應用程式開啟了特定的檔案,作為系統管理員,這將使得您能夠作出更好的決策。例如,您不應該解除安裝具有開啟檔案的檔案系統。使用 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 更高階的用法可以幫助您查詢刪除的檔案,並獲得關於網路連線的資訊。這是一個功能強大的工具,它幾乎可以用於任何地方。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-941676/,如需轉載,請註明出處,否則將追究法律責任。

相關文章