在 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』。
留言
張貼留言