在 Linux Kernel 中取得目錄中的檔案清單
寫一支程式在 User Space 下列出目錄中的檔案清單相當容易,我們可以用 readdir() 去一項項取得檔案內容,事實上, readdir() 是由 Linux Kernel VFS(Virtual Filesystem) 所提供的 API,真正的實作在檔案系統的核心模組(Kernel Module)中。然而因為 Kernel readdir() 的相關實作牽涉到 Kernel/User space 的資料交換問題,所以如果我們是在撰寫 Kernel 的驅動程式,當然就不能使用這系列的 APIs。不過也因為所有的資訊都存在於 Kernel 的資料結構中,我們可以直接從記憶體中的 Linked list 中快速取得檔案清單。
在取得某個目錄下所有檔案資訊前,要先得到該目錄的敘述 struct dentry,VFS 用 struct dentry 的串聯組合來描述整個檔案系統的目錄結構,檔案清單則被保存在 dentry 中的 Double Linked List,因此其實只要知道如何使用 Kernel 提供的 Double Linked list,就能得到該目錄下所有的檔案資訊。而這邊要注意的是 Kernel 提供的一個通用 Linked list 結構(struct list_head),用來統一整個核心的 Linked list 操作機制,這檔案清單的 Double Linked List 就是使用這通用的結構。
這裡是 Linux Kernel 2.6.37 中 dentry 的資料結構(定義在 linux/dcache.h,其中較不重要的部份以『...』做省略,此外,某些定義與較早版本的 Kernel 有所差異,將在文章最後補上說明):
由 struct dentry 可以發現,利用讀取 d_u.d_child 這個 Linked List 就可以取得底下所有的檔案資訊。實際做法可以參考範例程式碼,此程式會列出某個目錄下所有的檔案名稱:
於 dentry 裡保存檔案名稱的資料結構是 struct qst,其定義如下(定義與較早版本的 Kernel 有所差異,將在文章最後補上說明):
後記
更多操作 struct list_head 請參考相關的書籍或網路文件,這部份很重要,因為在 Kernel 處處都會發現 struct list_head 的蹤影。
在較早的 Linux Kernel 版本中 dentry 的 d_child 並未使用 union d_u,是這樣被定義的(網路上找到的版本也多半是這樣):
而早期的 struct qstr 是這樣定義:
由於 Kernel 版本數量太多,且定義會不斷被改變,若在實際撰寫時請參考 Linux 核心程式原始碼的『 include/linux/dcache.h』。
在取得某個目錄下所有檔案資訊前,要先得到該目錄的敘述 struct dentry,VFS 用 struct dentry 的串聯組合來描述整個檔案系統的目錄結構,檔案清單則被保存在 dentry 中的 Double Linked List,因此其實只要知道如何使用 Kernel 提供的 Double Linked list,就能得到該目錄下所有的檔案資訊。而這邊要注意的是 Kernel 提供的一個通用 Linked list 結構(struct list_head),用來統一整個核心的 Linked list 操作機制,這檔案清單的 Double Linked List 就是使用這通用的結構。
這裡是 Linux Kernel 2.6.37 中 dentry 的資料結構(定義在 linux/dcache.h,其中較不重要的部份以『...』做省略,此外,某些定義與較早版本的 Kernel 有所差異,將在文章最後補上說明):
struct dentry { ...省略 struct hlist_node d_hash; /* lookup hash list */ struct dentry *d_parent; /* parent directory */ struct qstr d_name; struct list_head d_lru; /* LRU list */ /* * d_child and d_rcu can share memory */ union { struct list_head d_child; /* child of parent list */ struct rcu_head d_rcu; } d_u; struct list_head d_subdirs; /* our children */ struct list_head d_alias; /* inode alias list */ ...省略 };
由 struct dentry 可以發現,利用讀取 d_u.d_child 這個 Linked List 就可以取得底下所有的檔案資訊。實際做法可以參考範例程式碼,此程式會列出某個目錄下所有的檔案名稱:
struct list_head *next; struct dentry *child_dentry; list_for_each(next, &file->f_path.dentry->d_u.d_child) { child_dentry = (struct dentry *)list_entry(next, struct dentry, d_u.d_child); printk("%s\n", child_dentry->d_name.name); }
於 dentry 裡保存檔案名稱的資料結構是 struct qst,其定義如下(定義與較早版本的 Kernel 有所差異,將在文章最後補上說明):
struct qstr { unsigned int hash; unsigned int len; const unsigned char *name; };
後記
更多操作 struct list_head 請參考相關的書籍或網路文件,這部份很重要,因為在 Kernel 處處都會發現 struct list_head 的蹤影。
在較早的 Linux Kernel 版本中 dentry 的 d_child 並未使用 union d_u,是這樣被定義的(網路上找到的版本也多半是這樣):
struct dentry { ...省略 struct dentry * d_parent; struct list_head d_hash; struct list_head d_lru; struct list_head d_child; struct list_head d_subdirs; ...省略 }
而早期的 struct qstr 是這樣定義:
struct qstr { const unsigned char * name; unsigned int len; unsigned int hash; char name_str[0]; };
由於 Kernel 版本數量太多,且定義會不斷被改變,若在實際撰寫時請參考 Linux 核心程式原始碼的『 include/linux/dcache.h』。
留言
張貼留言