問題背景
一個內核模塊中,需要通過d_path接口獲取文件的路徑,然后與目標文件白名單做匹配。
在生產環境中,獲取的文件是存在的,但是與文件白名單中的文件總是匹配失敗。
問題定位:
通過打印d_path返回的字符串,發現獲得的路徑后面多了一個" (deleted)"字符串,在做完全匹配時不通過。
看了d_path函數說明:如果entry被刪除了,會添加" (deleted)"字符串。
* Convert a dentry into an ASCII path name. If the entry has been deleted
* the string " (deleted)" is appended. Note that this is ambiguous.
為什么會這樣,還要從Linux文件系統的dcache、inode和dentry說起。
linux文件系統
Linux能夠成功的其中一個關鍵是它能夠很好地與其他系統共存。你可以透明地掛載由Windows或其他Unix系統格式化
的磁盤或者分區。Linux與其他Unix系統一樣,可以通過虛擬文件系統管理支持多種文件系統類型(Virtual Filesystem)。
內核中虛擬文件系提供了能夠表示許多不同類型文件系統的廣泛信息;Linux有字段或者函數能夠支持所有真實文件系統
的每一個操作。對於每個read、write或其他調用的函數,內核替代真正的函數來支持本地Linux文件系統,NTFS文件系統,
或者其他任何的文件系統。
有一張圖,可以表示進程與VFS各種對象之間的關系,來源於《深入理解Linux內核》,如下所示:
superblock對象:
存儲被掛載的文件系統的相關信息。對於磁盤文件系統,它就是文件系統控制塊(filesystem control block)。
inode對象
存儲某一特定文件的一般信息。對於磁盤文件系統,它就是文件控制塊(file control block)。每個inode對象
都有一個inode數字,它唯一確定了文件系統內的文件。
file對象
存儲進程與其打開的文件之間的互操作信息。這些信息僅存在於內核內存中,在進程打開文件期間有效。
dentry對象
存儲相關文件的目錄條目(directory entry,也就是特殊的文件)的鏈接信息。每個磁盤文件系統都有自己的
特別方式存儲這些信息。
從上圖可以看到,在文件對象和dentry對象之間,還有一個dentry cache(簡稱dcache),它是文件系統性能
的一個關鍵角色。所有最近使用的dentry對象都包含在磁盤cache中,它叫做dentry cache。它能加快從文件路徑到
最后一個路徑組件的轉換。
通常來說,磁盤cache是軟件機制,它允許內核在RAM中保存一些存儲在磁盤上的信息。因此,它可以很好地
滿足對這些數據的進一步快速訪問,而不需要通過磁盤本身做慢速訪問。
Page Frame回收機制
上面也提到,dentry cache是軟件機制,保存最近訪問文件的dentry對象,那它就不會是無限擴大,系統總是會
根據實際情況做dentry的回收和老化。
在Linux系統中,由PFRA(Page Frame Reclaiming Algorithm)決定dentry cache回收。當它嘗試重新申請Page
Frame,它也要檢查dentry cache是否需要收縮。
Linux系統PFRA從disk cache回收page的注冊函數是shrink_slab,由try_to_free_pages-->shrink_slab調用。
總結:
由上面可以得出,當系統通過PFRA釋放了dentry條目,就會把dentry從disk cache鏈表中刪除,但是其值還在,
所以在d_path中,會得到帶" (deleted)"的字符串。
教訓&經驗
內核代碼接口很強大,學藝不精,導致問題
使用內核接口之前,要仔細研讀函數說明,了解細節和應用場景
參考自己的應該場景,做適當的處理。
參考資料:
1.《深入理解Linux內核》(第三版)