1. 程式人生 > >使用者空間與核心空間資料交換的方式(1)------debugfs

使用者空間與核心空間資料交換的方式(1)------debugfs

轉載自https://www.cnblogs.com/hoys/archive/2011/04/10/2011124.html

核心開發者經常需要向用戶空間應用輸出一些除錯資訊,在穩定的系統中可能根本不需要這些除錯資訊,但是在開發過程中,為了搞清楚核心的行為,除錯資訊非常必要,printk可能是用的最多的,但它並不是最好的,除錯資訊只是在開發中用於除錯,而printk將一直輸出,因此開發完畢後需要清除不必要 的printk語句,另外如果開發者希望使用者空間應用能夠改變核心行為時,printk就無法實現。因此,需要一種新的機制,那只有在需要的時候使用,它在需要時通過在一個虛擬檔案系統中建立一個或多個檔案來向用戶空間應用提供除錯資訊。
有幾種方式可以實現上述要求:
 (1)使用procfs,在/proc建立檔案輸出除錯資訊,但是procfs對於大於一個記憶體頁(對於x86是4K)的輸出比較麻煩,而且速度慢,有時回出現一些意想不到的問題。
 (2)使用sysfs(2.6核心引入的新的虛擬檔案系統),在很多情況下,除錯資訊可以存放在那裡,但是sysfs主要用於系統管理,它希望每一個檔案對應核心的一個變數,如果使用它輸出複雜的資料結構或除錯資訊是非常困難的。
 (3)使用libfs建立一個新的檔案系統,該方法極其靈活,開發者可以為新檔案系統設定一些規則,使用libfs使得建立新檔案系統更加簡單,但是仍然超出了一個開發者的想象。
 (4)為了使得開發者更加容易使用這樣的機制,Greg Kroah-Hartman開發了debugfs(在2.6.11中第一次引入),它是一個虛擬檔案系統,專門用於輸出除錯資訊,該檔案系統非常小,很容易使用,可以在配置核心時選擇是否構件到核心中,在不選擇它的情況下,使用它提供的API的核心部分不需要做任何改動。


使用debugfs的開發者首先需要在檔案系統中建立一個目錄,下面函式用於在debugfs檔案系統下建立一個目錄:

struct dentry  *debugfs_create_dir(const char *name, struct dentry *parent);

引數name是要建立的目錄名,引數parent指定建立目錄的父目錄的dentry,如果為NULL,目錄將建立在debugfs檔案系統的根目錄下。如果返回為-ENODEV,表示核心沒有把debugfs編譯到其中,如果返回為NULL,表示其他型別的建立失敗,如果建立目錄成功,返回指向該 目錄對應的dentry條目的指標。
下面函式用於在debugfs檔案系統中建立一個檔案: 

struct dentry  *debugfs_create_file(const char *name, mode_t mode, struct  dentry *parent,
                                    void *data, struct  file_operations *fops);

引數name指定要建立的檔名,引數mode指定該檔案的訪問許可,引數parent指向該檔案所在目錄,引數data為該檔案特定的一些資料, 引數fops為實現在該檔案上進行檔案操作的fiel_operations結構指標,在很多情況下,由seq_file提供的檔案操作實現就足夠了,因此使用debugfs很容易,當然,在一些情況下,開發者可能僅需要使用使用者應用可以控制的變數來除錯,debugfs也提供了4個這樣的API方便開發者使用:

struct dentry *debugfs_create_u8(const char *name, mode_t mode, struct  dentry *parent, 
                                 u8 *value);
struct dentry *debugfs_create_u16(const char *name, mode_t mode, struct dentry *parent, u16 
                                  *value);
struct dentry *debugfs_create_u32(const char *name, mode_t mode, struct dentry *parent, u32 
                                  *value);
struct dentry *debugfs_create_bool(const char *name, mode_t mode, struct dentry *parent, 
                                   u32 *value);

引數name和mode指定檔名和訪問許可,引數value為需要讓使用者應用控制的核心變數指標。
當核心模組解除安裝時,Debugfs並不會自動清除該模組建立的目錄或檔案,因此對於建立的每一個檔案或目錄,開發者必須呼叫下面函式清除:

void debugfs_remove(struct dentry *dentry);

引數dentry為上面建立檔案和目錄的函式返回的dentry指標。
在下面給出了一個使用debufs的示例模組debugfs_exam.c,為了保證該模組正確執行,必須讓核心支援debugfs, debugfs是一個除錯功能,因此它位於主選單Kernel hacking,並且必須選擇Kernel debugging選項才能選擇,它的選項名稱為Debug Filesystem。為了在使用者態使用debugfs,使用者必須mount它,下面是在作者系統上的使用輸出:

  $ mkdir -p  /debugfs
  $ mount -t debugfs debugfs /debugfs
  $ insmod  ./debugfs_exam.ko
  $ ls /debugfs
  debugfs-exam
  $ ls /debugfs/debugfs-exam
  u8_var         u16_var        u32_var        bool_var
  $ cd /debugfs/debugfs-exam
  $ cat u8_var
  $ echo 200 > u8_var
  $ cat u8_var
  $ cat bool_var
  N
  $ echo 1 > bool_var
  $ cat bool_var
  Y
//kernel module: debugfs_exam.c
#include <linux/config.h>
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/types.h>

/*dentry:目錄項,是Linux檔案系統中某個索引節點(inode)的連結。這個索引節點可以是檔案,也可以是目錄。
Linux用資料結構dentry來描述fs中和某個檔案索引節點相連結的一個目錄項(能是檔案,也能是目錄)。
  (1)未使用(unused)狀態:該dentry物件的引用計數d_count的值為0,但其d_inode指標仍然指向相關的的索引節點。該目錄項仍然包含有效的資訊,只是當前沒有人引用他。這種dentry物件在回收記憶體時可能會被釋放。
  (2)正在使用(inuse)狀態:處於該狀態下的dentry物件的引用計數d_count大於0,且其d_inode指向相關的inode物件。這種dentry物件不能被釋放。
  (3)負(negative)狀態:和目錄項相關的inode物件不復存在(相應的磁碟索引節點可能已被刪除),dentry物件的d_inode指標為NULL。但這種dentry物件仍然儲存在dcache中,以便後續對同一檔名的查詢能夠快速完成。這種dentry物件在回收記憶體時將首先被釋放。
*/
static struct dentry *root_entry, *u8_entry, *u16_entry, *u32_entry, *bool_entry;
static u8 var8;
static u16 var16;
static u32 var32;
static u32 varbool;

static int __init exam_debugfs_init(void)
{

        root_entry = debugfs_create_dir("debugfs-exam", NULL);
        if (!root_entry) {
                printk("Fail to create proc dir: debugfs-exam\n");
                return 1;
        }

        u8_entry = debugfs_create_u8("u8-var", 0644, root_entry, &var8);
        u16_entry = debugfs_create_u16("u16-var", 0644, root_entry, &var16);
        u32_entry = debugfs_create_u32("u32-var", 0644, root_entry, &var32);
        bool_entry = debugfs_create_bool("bool-var", 0644, root_entry, &varbool);

        return 0;
}

static void __exit exam_debugfs_exit(void)
{
        debugfs_remove(u8_entry);
        debugfs_remove(u16_entry);
        debugfs_remove(u32_entry);
        debugfs_remove(bool_entry);
        debugfs_remove(root_entry);
}

module_init(exam_debugfs_init);
module_exit(exam_debugfs_exit);
MODULE_LICENSE("GPL");