天天看點

Linux核心裡的DebugFS

debugfs,顧名思義,是一種用于核心調試的虛拟檔案系統,核心開發者通過debugfs和使用者空間交換資料。類似的虛拟檔案系統還有procfs和sysfs等,這幾種虛拟檔案系統都并不實際存儲在硬碟上,而是linux核心運作起來後才建立起來。

通常情況下,最常用的核心調試手段是printk。但printk并不是所有情況都好用,比如列印的資料可能過多,我們真正關心的資料在大量的輸出裡不是那麼一目了然;或者我們在調試時可能需要修改某些核心變量,這種情況下printk就無能為力,而如果為了修改某個值重新編譯核心或者驅動又過于低效,此時就需要一個臨時的檔案系統可以把我們需要關心的資料映射到使用者空間。在過去,procfs可以實作這個目的,到了2.6時代,新引入的sysfs也同樣可以實作,但不論是procfs或是sysfs,用它們來實作某些debug的需求,似乎偏離了它們建立的本意。比如procfs,其目的是反映程序的狀态資訊;而sysfs主要用于linux裝置模型。不論是procfs或是sysfs的接口應該保持相對穩定,因為使用者态程式很可能會依賴它們。當然,如果我們隻是臨時借用procfs或者sysfs來作debug之用,在代碼釋出之前将相關調試代碼删除也無不可。但如果相關的調試借口要在相當長的一段時間記憶體在于核心之中,就不太适合放在procfs和sysfs裡了。故此,debugfs應運而生。

預設情況下,debugfs會被挂載在目錄/sys/kernel/debug之下,如果您的發行版裡沒有自動挂載,可以用如下指令手動完成:

# mount -t debugfs none /your/debugfs/dir 

linux核心為debugfs提供了非常簡潔的api,本文接下來将以一個實作為例來介紹,sample code可以從這裡下載下傳。

這個實作會在debugfs中建立如下的目錄結構:

Linux核心裡的DebugFS

其中,a對應子產品中的一個u8類型的變量,b和subdir下面的c都是對應子產品裡的一個字元數組,隻是它們的實作方式不同。

在module_init裡,我們首先要建立根目錄mydebug:

my_debugfs_root = debugfs_create_dir("mydebug", null); 

第一個參數是目錄的名稱,第二個參數用來指定這個目錄的上級目錄,如果是null,則表示是放在debugfs的根目錄裡。

子目錄也是用debugfs_create_dir來實作:

sub_dir = debugfs_create_dir("subdir", my_debugfs_root); 

建立檔案a的代碼非常簡單:

debugfs_create_u8("a", 0644, my_debugfs_root, &a); 

這表示檔案名為“a”,檔案屬性是0644,父目錄是上面建立的“mydebug”,對應的變量是子產品中的a。

linux核心還提供了其他一些建立debugfs檔案的api,請參考本文的附錄。

b是一個32-bytes的字元數組,在debugfs裡,數組可以用blob wrapper來實作。

char hello[32] = "hello world!\n"; 

struct debugfs_blob_wrapper b; 

b.data = (void *)hello; 

b.size = strlen(hello) + 1; 

debugfs_create_blob("b", 0644, my_debugfs_root, &b);  

這裡需要注意的是,blob wrapper定義的資料隻能是隻讀的。在本例中,雖然我們把檔案b的權限設定為0644,但實際這個檔案還是隻讀的,如果試圖改寫這個檔案,系統将提示出錯。

如果需要對核心數組進行寫的動作,blob wrapper就無法滿足要求,我們隻能通過自己定義檔案操作來實作。在這個實作裡,可以參考檔案c的實作。c和b在子產品裡對應着同一塊字元數組,不同的是,b是隻讀的,而c通過自定義的檔案操作同時實作了讀和寫。

static int c_open(struct inode *inode, struct file *filp) 

    filp->private_data = inode->i_private; 

    return 0; 

static ssize_t c_read(struct file *filp, char __user *buffer, 

        size_t count, loff_t *ppos) 

    if (*ppos >= 32) 

        return 0; 

    if (*ppos + count > 32) 

        count = 32 - *ppos; 

    if (copy_to_user(buffer, hello + *ppos, count)) 

        return -efault; 

    *ppos += count; 

    return count; 

static ssize_t c_write(struct file *filp, const char __user *buffer, 

    if (copy_from_user(hello + *ppos, buffer, count)) 

struct file_operations c_fops = { 

    .owner = this_module, 

    .open = c_open, 

    .read = c_read, 

    .write = c_write, 

}; 

debugfs_create_file("c", 0644, sub_dir, null, &c_fops);  

注:代碼裡,c_open其實并沒有任何用處,因為c_read和c_write直接引用了全局變量hello。這裡,我們也可以換一種寫法,在read/write函數裡用filp->private_data來引用字元數組hello。

到這裡,三個檔案和子目錄已經建立完畢。在module_exit中,我們要記得釋放建立的資料。

debugfs_remove_recursive(my_debugfs_root); 

debugfs_remove_recursive可以幫我們逐漸移除每個配置設定 

本文作者:佚名

來源:51cto

繼續閱讀