從 lsof 開始,深入理解 Linux 虛擬檔案系統

大雄45發表於2022-01-30
導讀 有時會出現這樣的情況,磁碟空間顯示已經被佔滿,但是在檢視磁碟的具體檔案佔用情況時,發現磁碟仍然有很大的空餘空間。

從 lsof 開始,深入理解 Linux 虛擬檔案系統從 lsof 開始,深入理解 Linux 虛擬檔案系統

背景

有時會出現這樣的情況,磁碟空間顯示已經被佔滿,但是在檢視磁碟的具體檔案佔用情況時,發現磁碟仍然有很大的空餘空間。

1.執行df 檢視磁碟使用情況,發現磁碟已經滿了。

-bash-4.2$ df -Th
Filesystem Type Size Used Avail Use% Mounted on
/dev/vda1 ext4 30G 30G 0 100% /
devtmpfs devtmpfs 489M 0 489M 0% /dev
tmpfs tmpfs 497M 0 497M 0% /dev/shm
tmpfs tmpfs 497M 50M 447M 11% /run
tmpfs tmpfs 497M 0 497M 0% /sys/fs/cgroup

2.執行 du  檢視各個目錄的磁碟佔用情況,把各個目錄檔案的大小相加,發現並沒有佔滿磁碟,有10多G空間莫名失蹤。

-bash-4.2$ du -h --max-depth=1 /home16M /home/logs11G /home/serverdog11G /home

3.為何會出現這樣的情況呢?

因為雖然檔案已被刪除,但是一些程式仍然開啟這些檔案,因此其佔用的磁碟空間並沒有被釋放。執行lsof 命令顯示開啟已刪除的檔案。將有問題的程式重啟(或,清空),磁碟空間就會得到釋放。

-bash-4.2# lsof | grep deletemysqld 2470 mysql 4u REG 253,1 0 523577 /var/tmp/ibfTeQFn (deleted)
mysqld 2470 mysql 5u REG 253,1 0 523579 /var/tmp/ibaHcIdW (deleted)
mysqld 2470 mysql 6u REG 253,1 0 523581 /var/tmp/ibLjiALu (deleted)
mysqld 2470 mysql 7u REG 253,1 0 523585 /var/tmp/ibCFnzTB (deleted)
mysqld 2470 mysql 11u REG 253,1 0 523587 /var/tmp/ibCjuqva (deleted)

那麼,  的檔案系統,到底為什麼這麼設計呢?要了解這些,就要先弄清楚並不容易,下面將從一些基本概念入手,一步步將這些梳理清楚:

  1. 什麼是虛擬檔案系統(VFS:virtual filesystem)?
  2. 什麼是通用檔案模型?
  3. 超級塊物件(superblock object)
  4. 索引節點物件(inode object)
  5. 檔案物件(file object)
  6. 目錄項物件(dentry object)
  7. 檔案的概念
  8. 檔案的表達
  9. 記憶體表達
  10. 磁碟表達
  11. 目錄樹的構建
  12. 軟連結 vs 硬連結
  13. 檔案 & 磁碟管理
  14. 索引節點狀態
  15. 檔案 & 程式管理
  16. 操作:
  17. 開啟&刪除
虛擬檔案系統(virtual filesystem)

下圖顯示了 Linux 作業系統中負責檔案管理的基本元件。上半區域為使用者模式,下半區域為核心模式。應用程式使用標準庫libc來訪問檔案,庫將請求對映到系統呼叫,以便進入核心模式。

從 lsof 開始,深入理解 Linux 虛擬檔案系統從 lsof 開始,深入理解 Linux 虛擬檔案系統

所有與檔案相關的操作的入口都是虛擬檔案系統(VFS),而非特定的額檔案系統(如Ext3、ReiserFS和NFS)。VFS 提供了系統庫和特定檔案系統之間的介面。因此,VFS 不僅充當抽象層,而且實際上它提供了一個檔案系統的基本實現,可以由不同的實現來使用和擴充套件。因此,要了解檔案系統是如何工作的,就要先了解VFS 。

通用檔案模型

VFS 的主要思想在於引入了一個通用檔案模型(common file model)。通用檔案模型由以下物件型別組成:

超級塊物件(superblock object)

記憶體:檔案系統安裝時建立,存放檔案系統的有關資訊

磁碟:對應於存放在磁碟上的檔案系統控制塊(filesystem control block)

索引節點物件(inode object)

記憶體:訪問時建立,存放關於具體檔案的一般資訊(inode 結構)

磁碟:對應於存放在磁碟上的檔案控制塊(file control block)

每個索引節點物件都有一個索引節點號,唯一地標識檔案系統的檔案

檔案物件(file object)

記憶體:開啟檔案時建立,存放 開啟檔案 與程式之間進行互動的有關資訊(file 結構)

開啟檔案資訊,僅當程式訪問檔案期間存在於核心記憶體中。

目錄項物件(dentry object)

記憶體:目錄項一旦被讀入記憶體,VFS就會將其轉換成dentry 結構的目錄項物件

磁碟:特定檔案系統以特定的方式儲存在磁碟上

存放目錄項(即,檔名稱)與對應檔案進行連結的有關資訊

目錄樹

綜合來說,Linux 的 根檔案系統(system’s root filessystem) 是核心啟動mount的第一個檔案系統。核心程式碼映像檔案儲存在根檔案系統中,而系統引導啟動程式會在根檔案系統掛載之後,從中把一些基本的初始化 和服務等載入到記憶體中去執行(檔案系統和核心是完全獨立的兩個部分)。其他檔案系統,則後續透過 或命令作為子檔案系統安裝在已安裝檔案系統的目錄上,最終形成整個目錄樹。

start_kernel
vfs_caches_init
mnt_init
init_rootfs // 註冊rootfs檔案系統
init_mount_tree // 掛載rootfs檔案系統
…
rest_init
kernel_thread(kernel_init, NULL, CLONE_FS);

就單個檔案系統而言,在檔案系統安裝時,建立超級塊物件;沿樹查詢檔案時,總是首先從初識目錄的中查詢匹配的目錄項,以便獲取相應的索引節點,然後讀取索引節點的目錄檔案,轉化為dentry物件,再檢查匹配的目錄項,反覆執行以上過程,直至找到對應的檔案的索引節點,並建立索引節點物件。

軟連結 vs 硬連結

軟連結是一個普通的檔案,其中存放的是另外一個檔案的路徑名。硬連結則指向同一個索引節點,硬連結數記錄在索引節點物件的 i_nlink 欄位。當i_nlink欄位為零時,說明沒有硬連結指向該檔案。

檔案 & 程式管理

下圖是一個簡單示例,說明程式是怎樣與檔案進行互動。三個不同程式開啟同一個檔案,每個程式都有自己的檔案物件,其中兩個程式使用同一個硬連結(每個硬連結對應一個目錄物件),兩個目錄項物件都指向同一個 索引節點物件。

從 lsof 開始,深入理解 Linux 虛擬檔案系統從 lsof 開始,深入理解 Linux 虛擬檔案系統

索引節點的資料又由兩部分組成:記憶體資料和磁碟資料。Linux 使用 Write back 作為索引節點的資料一致性策略。對於索引節點的資料,當檔案被開啟時,才會載入索引節點到記憶體;當不再被程式使用,則從記憶體踢出;如果中間有更新,則需要把資料寫回磁碟。

* "in_use" - valid inode, i_count > 0, i_nlink > 0
* "dirty" - as "in_use" but also dirty
* "unused" - valid inode, i_count = 0

索引節點是否仍在使用,是透過 open() 和 close() 操作建立和銷燬檔案物件,檔案物件透過索引節點提供的 iget 和 iput 更新索引節點的i_count欄位,以完成使用計數。open 操作使得 i_count 加一, close 操作使得 i_count 減一。在 close 操作時判斷索引節點是否釋放,如果 i_count = 0,則意味著不再有程式引用,將會從記憶體釋放。

檔案 & 磁碟管理
檔案與磁碟管理聯絡最緊密的操作,莫過於touch和rm操作,而尤以後者最為關鍵。透過strace(或 dtruss),檢視 rm 的實際的系統呼叫

# dtruss rm tmp
...
geteuid(0x0, 0x0, 0x0) = 0 0
ioctl(0x0, 0x4004667A, 0x7FFEE06F09C4) = 0 0
lstat64("tmp\0", 0x7FFEE06F0968, 0x0) = 0 0
access("tmp\0", 0x2, 0x0) = 0 0
unlink("tmp\0", 0x0, 0x0) = 0 0

可以發現 rm 實際是透過 unlink 完成的。unlink代表刪除目錄項,以及減少其索引節點的計數。由通用檔案模型可知,父目錄本身同樣是一個檔案,也就意味著目錄項是其檔案資料的一部分。刪除目錄項等價於從父目錄的檔案中刪除資料,也就意味著首先要開啟父目錄的檔案。那麼,刪除操作即可理解為:

  1. 刪除命令(一個程式)使用 open 操作獲得父目錄檔案物件
  2. 透過 iget 增加 目錄檔案的索引節點物件計數
  3. 讀取目錄檔案資料
  4. 將目錄檔案資料轉化為目錄項物件
  5. 由於目錄項包含檔案的索引節點,類似的,需要透過 iget 增加檔案的索引節點物件計數
  6. 刪除目錄的目錄項
  7. 減少檔案索引節點物件的硬連結計數i_nlink
  8. 透過 iput 結束對檔案索引節點物件的操作,使用計數 i_count 減一
  9. 判斷i_count是否為零,如果為零,則釋放記憶體
  10. 然後,判斷i_nlink是否為零,如果為零,則釋放磁碟空間
  11. 透過 iput 結束對目錄索引節點物件的操作。
總結

回頭來看遇到的問題,其實可以從兩個角度來理解:

索引與資料

檔案系統與檔案、磁碟管理與檔案、程式管理與檔案,最核心的都是檔案的索引,而不是檔案的資料。把資料和索引分開是理解檔案系統的關鍵。
從 lsof 開始,深入理解 Linux 虛擬檔案系統從 lsof 開始,深入理解 Linux 虛擬檔案系統

快取策略

由於作業系統使用 Write back 的策略,意味著只有先釋放記憶體,才有可能釋放磁碟。

Why lsof ?

從上面的模型可以很清楚的理解,因為目錄已經沒有索引到檔案了,但是開啟檔案還有索引到檔案,所以不能立刻釋放磁碟空間。

為什麼 lsof 可以找到已刪除未釋放的檔案呢?

lsof,顧名思義:list open files,該命令的原理就是查詢開啟檔案的列表,因此可以找到已刪除未釋放的檔案。

原文來自:

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

相關文章