內(nèi)核開發(fā)者經(jīng)常需要向用戶空間應(yīng)用輸出一些調(diào)試信息,在穩(wěn)定的系統(tǒng)中可能根本不需要這些調(diào)試信息,但是在開發(fā)過程中,為了搞清楚內(nèi)核的行為,調(diào)試信息非常必要,printk可能是用的最多的,但它并不是最好的,調(diào)試信息只是在開發(fā)中用于調(diào)試,而printk將一直輸出,因此開發(fā)完畢后需要清除不必要 的printk語句,另外如果開發(fā)者希望用戶空間應(yīng)用能夠改變內(nèi)核行為時,printk就無法實現(xiàn)。因此,需要一種新的機制,那只有在需要的時候使用,它在需要時通過在一個虛擬文件系統(tǒng)中創(chuàng)建一個或多個文件來向用戶空間應(yīng)用提供調(diào)試信息。
有幾種方式可以實現(xiàn)上述要求:
(1)使用procfs,在/proc創(chuàng)建文件輸出調(diào)試信息,但是procfs對于大于一個內(nèi)存頁(對于x86是4K)的輸出比較麻煩,而且速度慢,有時回出現(xiàn)一些意想不到的問題。
(2)使用sysfs(2.6內(nèi)核引入的新的虛擬文件系統(tǒng)),在很多情況下,調(diào)試信息可以存放在那里,但是sysfs主要用于系統(tǒng)管理,它希望每一個文件對應(yīng)內(nèi)核的一個變量,如果使用它輸出復(fù)雜的數(shù)據(jù)結(jié)構(gòu)或調(diào)試信息是非常困難的。
(3)使用libfs創(chuàng)建一個新的文件系統(tǒng),該方法極其靈活,開發(fā)者可以為新文件系統(tǒng)設(shè)置一些規(guī)則,使用libfs使得創(chuàng)建新文件系統(tǒng)更加簡單,但是仍然超出了一個開發(fā)者的想象。
(4)為了使得開發(fā)者更加容易使用這樣的機制,Greg Kroah-Hartman開發(fā)了debugfs(在2.6.11中第一次引入),它是一個虛擬文件系統(tǒng),專門用于輸出調(diào)試信息,該文件系統(tǒng)非常小,很容易使用,可以在配置內(nèi)核時選擇是否構(gòu)件到內(nèi)核中,在不選擇它的情況下,使用它提供的API的內(nèi)核部分不需要做任何改動。
使用debugfs的開發(fā)者首先需要在文件系統(tǒng)中創(chuàng)建一個目錄,下面函數(shù)用于在debugfs文件系統(tǒng)下創(chuàng)建一個目錄:
struct dentry *debugfs_create_dir(const char *name, struct dentry *parent);
參數(shù)name是要創(chuàng)建的目錄名,參數(shù)parent指定創(chuàng)建目錄的父目錄的dentry,如果為NULL,目錄將創(chuàng)建在debugfs文件系統(tǒng)的根目錄下。如果返回為-ENODEV,表示內(nèi)核沒有把debugfs編譯到其中,如果返回為NULL,表示其他類型的創(chuàng)建失敗,如果創(chuàng)建目錄成功,返回指向該 目錄對應(yīng)的dentry條目的指針。
下面函數(shù)用于在debugfs文件系統(tǒng)中創(chuàng)建一個文件:
struct dentry *debugfs_create_file(const char *name, mode_t mode, struct dentry *parent,
void *data, struct file_operations *fops);
參數(shù)name指定要創(chuàng)建的文件名,參數(shù)mode指定該文件的訪問許可,參數(shù)parent指向該文件所在目錄,參數(shù)data為該文件特定的一些數(shù)據(jù), 參數(shù)fops為實現(xiàn)在該文件上進行文件操作的fiel_operations結(jié)構(gòu)指針,在很多情況下,由seq_file提供的文件操作實現(xiàn)就足夠了,因此使用debugfs很容易,當(dāng)然,在一些情況下,開發(fā)者可能僅需要使用用戶應(yīng)用可以控制的變量來調(diào)試,debugfs也提供了4個這樣的API方便開發(fā)者使用:
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);
參數(shù)name和mode指定文件名和訪問許可,參數(shù)value為需要讓用戶應(yīng)用控制的內(nèi)核變量指針。
當(dāng)內(nèi)核模塊卸載時,Debugfs并不會自動清除該模塊創(chuàng)建的目錄或文件,因此對于創(chuàng)建的每一個文件或目錄,開發(fā)者必須調(diào)用下面函數(shù)清除:
void debugfs_remove(struct dentry *dentry);
參數(shù)dentry為上面創(chuàng)建文件和目錄的函數(shù)返回的dentry指針。
在下面給出了一個使用debufs的示例模塊debugfs_exam.c,為了保證該模塊正確運行,必須讓內(nèi)核支持debugfs, debugfs是一個調(diào)試功能,因此它位于主菜單Kernel hacking,并且必須選擇Kernel debugging選項才能選擇,它的選項名稱為Debug Filesystem。為了在用戶態(tài)使用debugfs,用戶必須mount它,下面是在作者系統(tǒng)上的使用輸出:
$ 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
0
$ echo 200 》 u8_var
$ cat u8_var
200
$ 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文件系統(tǒng)中某個索引節(jié)點(inode)的鏈接。這個索引節(jié)點可以是文件,也可以是目錄。
Linux用數(shù)據(jù)結(jié)構(gòu)dentry來描述fs中和某個文件索引節(jié)點相鏈接的一個目錄項(能是文件,也能是目錄)。
(1)未使用(unused)狀態(tài):該dentry對象的引用計數(shù)d_count的值為0,但其d_inode指針仍然指向相關(guān)
的的索引節(jié)點。該目錄項仍然包含有效的信息,只是當(dāng)前沒有人引用他。這種dentry對象在回收內(nèi)存時可能會被釋放。
(2)正在使用(inuse)狀態(tài):處于該狀態(tài)下的dentry對象的引用計數(shù)d_count大于0,且其d_inode指向相關(guān)
的inode對象。這種dentry對象不能被釋放。
(3)負(negative)狀態(tài):和目錄項相關(guān)的inode對象不復(fù)存在(相應(yīng)的磁盤索引節(jié)點可能已被刪除),dentry
對象的d_inode指針為NULL。但這種dentry對象仍然保存在dcache中,以便后續(xù)對同一文件名的查找能夠快速完成。
這種dentry對象在回收內(nèi)存時將首先被釋放。
*/
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”);
評論
查看更多