二、簡介
debugfs可用于內核向用戶空間提供信息,debugfs是個小型的文件系統,與/proc和sysfs不同,debugfs沒有較為嚴苛的規則和定義,我們可以在里面放置想要的任何信息,以便于系統開發和調試。
通常使用如下命令安裝debugfs:
mount-tdebugfsnone/sys/kernel/debug
或者:
mount-tdebugfsdebugfs/sys/kernel/debug/
也可以在/etc/fstab文件中使用等效的語句:
默認情況下,在一些發行版的linux系統中,只有root用戶可以訪問debugfs根目錄。
注意,在內核源碼中,debugfs API僅以GPL方式導出到模塊。
三、debugfs的API
1、在debugfs中創建目錄
使用debugfs的代碼應包含
structdentry*debugfs_create_dir(constchar*name,structdentry*parent);
當函數執行成功后,將在指定的父目錄下創建一個名為name的目錄,如果parent為NULL,則該目錄將在debugfs根目錄中創建。
函數執行成功后,返回一個指向struct dentry的指針,可用于在目錄中創建文件。
如果返回值為ERR_PTR(-ERROR)則表明出現了問題,如果返回ERR_PTR(-ENODEV),則表明內核是在沒有debugfs支持的情況下構建,這時候相關API將失效。
2、在debugfs目錄中創建文件
在debugfs目錄中創建文件的常用API是:
structdentry*debugfs_create_file(constchar*name,umode_tmode,structdentry*parent, void*data,conststructfile_operations*fops);
name是要創建文件的名稱。
mode描述了文件應具有的訪問權限。
parent表示應保存該文件的目錄,數據將存儲在生成的inode結構的i_private字段中。
fops是一個實現文件行為的一組文件操作。struct file_operations中包含了關于文件操作的很多接口函數,此處至少應提供read()和write()操作,其他操作可以根據實際情況實現。
該函數返回值將是指向所創建文件的dentry指針,如果發生錯誤,則返回ERR_PTR(-ERROR),如果缺少debugfs支持,則返回ERR_PTR(-ENODEV)。
3、創建一個具有初始大小的文件
創建一個具有初始大小的文件,可以使用以下API:
voiddebugfs_create_file_size(constchar*name,umode_tmode,structdentry*parent,void*data, conststructfile_operations*fops,loff_tfile_size);
file_size是初始文件大小,其他參數與函數debugfs_create_file相同。
4、創建包含單個整數值(十進制)的文件
在多數情況下,創建一組文件操作并不是必需的,這時候可以使用以下助手函數創建包含單個整數值的文件:
//創建包含u8整數值的文件 voiddebugfs_create_u8(constchar*name,umode_tmode,structdentry*parent,u8*value); //創建包含u16整數值的文件 voiddebugfs_create_u16(constchar*name,umode_tmode,structdentry*parent,u16*value); ////創建包含u32整數值的文件 voiddebugfs_create_u32(constchar*name,umode_tmode,structdentry*parent,u32*value); //創建包含u64整數值的文件 voiddebugfs_create_u64(constchar*name,umode_tmode,structdentry*parent,u64*value);
這些文件支持讀取和寫入給定值;如果不支持寫入特定文件,只需相應設置模式位即可,使用上述API創建的文件中的值是十進制的。
5、創建包含單個十六進制值的文件:
如果需要設置十六進制,可以使用以下API函數:
voiddebugfs_create_x8(constchar*name,umode_tmode,structdentry*parent,u8*value); voiddebugfs_create_x16(constchar*name,umode_tmode,structdentry*parent,u16*value); voiddebugfs_create_x32(constchar*name,umode_tmode,structdentry*parent,u32*value); voiddebugfs_create_x64(constchar*name,umode_tmode,structdentry*parent,u64*value);
只要我們知道要導出的值的大小,上述函數就非常有用。但是需要注意的是,某些類型在不同體系結構上可能具有不同的寬度。下列函數可以在這種特殊情況下提供幫助:
voiddebugfs_create_size_t(constchar*name,umode_tmode,structdentry*parent,size_t*value);
debugfs_create_size_t()函數將創建一個debugfs文件來表示size_t類型的變量。
6、創建包含unsigned long 類型的變量的文件
對于十進制和十六進制的 unsigned long 類型的變量可使用以下助手函數:
structdentry*debugfs_create_ulong(constchar*name,umode_tmode,structdentry*parent,unsignedlong*value); voiddebugfs_create_xul(constchar*name,umode_tmode,structdentry*parent,unsignedlong*value);
7、創建包含布爾類型的文件
對于布爾值可使用下列API函數:
voiddebugfs_create_bool(constchar*name,umode_tmode,structdentry*parent,bool*value);
讀取結果文件將產生Y(對于非零值)或N,后跟換行符。如果想要向該文件寫入數值,該文件將接收大寫或小寫值,或者1或0,其他任何的輸入都將被忽略。
8、創建包含atomic_t類型的值的文件
atomic_t值可使用以下API函數放置在debugfs中:
voiddebugfs_create_atomic_t(constchar*name,umode_tmode,structdentry*parent,atomic_t*value)
讀取該文件將獲取atomic_t值,寫入該文件將設置atomic_t值。
9、創建包含二進制數據塊的文件
也可以導出二進制數據塊,數據塊具有以下結構和功能:
structdebugfs_blob_wrapper{ void*data; unsignedlongsize; }; structdentry*debugfs_create_blob(constchar*name,umode_tmode,structdentry*parent,structdebugfs_blob_wrapper*blob);
如果想轉儲一個寄存器塊,debugfs提供了兩個函數:1、創建一個只有寄存器的文件。2、在另一個順序文件的中間位置插入一個寄存器塊:
structdebugfs_reg32{ char*name; unsignedlongoffset; }; structdebugfs_regset32{ conststructdebugfs_reg32*regs; intnregs; void__iomem*base; structdevice*dev;/*OptionaldeviceforRuntimePM*/ }; debugfs_create_regset32(constchar*name,umode_tmode,structdentry*parent,structdebugfs_regset32*regset); voiddebugfs_print_regs32(structseq_file*s,conststructdebugfs_reg32*regs,intnregs,void__iomem*base,char*prefix);
debugfs_print_regs32()中的base參數可能為0,但可能希望使用__stringify構建reg32數組,許多寄存器名(宏)實際上是寄存器基數上的字節偏移量。
10、創建u32數組的文件
如果想在debugfs中轉儲一個u32數組,可使用以下API:
structdebugfs_u32_array{ u32*array; u32n_elements; }; voiddebugfs_create_u32_array(constchar*name,umode_tmode,structdentry*parent,structdebugfs_u32_array*array);
array參數包裝了一個指向數組數據及其元素數量的指針。
注意:一旦數組被創建,它的大小不能被改變。
11、創建與設備相關的seq_file
有一個助手函數可用于創建與設備相關的seq_file:
voiddebugfs_create_devm_seqfile(structdevice*dev,constchar*name,structdentry*parent, int(*read_fn)(structseq_file*s,void*data));
dev參數是與這個debugfs文件相關的設備。
read_fn是一個函數指針,用于調用它來打印seq_file內容。
12、為debugfs中的文件重命名
如果想要重命名debugfs目錄下的文件名,可使用以下API:
structdentry*debugfs_rename(structdentry*old_dir,structdentry*old_dentry, structdentry*new_dir,constchar*new_name);
調用debugfs_rename()將為現有的debugfs文件(可能在不同的目錄中)提供一個新名稱,在調用debugfs_rename()之前必須不存在new_name,返回值是帶有更新后信息的old_dentry。
13、為debugfs目錄中的文件創建符號鏈接
符號鏈接可通過debugfs_create_symlink()創建:
structdentry*debugfs_create_symlink(constchar*name,structdentry*parent,constchar*target);
14、刪除debugfs創建的目錄或者文件
在debugfs中創建的所有目錄都不會自動清除。如果在沒有顯式刪除debugfs項的情況下卸載了一個模塊,這時結果將是出現大量過時的指針,還可能會出現一些奇怪的行為。因此,必須存在刪除創建的所有文件和目錄的操作和入口點。
可使用以下API刪除文件:
voiddebugfs_remove(structdentry*dentry);
如果dentry值是NULL或錯誤值,這時候將不會刪除任何內容。
使用下列API可以刪除整個目錄層級結構,在調試的時候可以使用:
voiddebugfs_remove_recursive(structdentry*dentry);
如果將指向與頂級目錄對應的dentry的指針傳遞給該debugfs_remove_recursive(),這時候該目錄下的整個層次結構將被刪除。
更多API可參見文末附上的參考鏈接。
四、實驗代碼
在本小節中,將使用上述提到的API在debugfs中創建目錄,并導出相應的參數描述文件,然后在命令行中對其進行查看,首先設計代碼:
/** *@filedebugfs_demo.c *@authoryourname(you@domain.com) *@briefdebugfsapiusage *@version0.1 *@date2023-08-17 * *@copyrightCopyright(c)2023 * */ #include#include #include #include #include #include #include #include #include #defineBUFFER_SIZE256 staticcharbuffer[BUFFER_SIZE]; staticstructdentry*debugfs_demo_dir; staticu8u8data=90; staticu32boolData=false; staticstructdentry*general_file,*u8data_dentry,*x8data_dentry,*bool_dentry; staticintgeneral_file_open(structinode*inode,structfile*file) { printk(KERN_INFO"dogeneral_file_openops "); return0; } staticssize_tgeneral_file_read(structfile*file,char__user*ubuf,size_tsize,loff_t*loff) { returnsimple_read_from_buffer(ubuf,size,loff,buffer,BUFFER_SIZE); } staticssize_tgeneral_file_write(structfile*file,constchar__user*ubuf,size_tsize,loff_t*loff) { if(size>BUFFER_SIZE)return-EINVAL; returnsimple_write_to_buffer(buffer,BUFFER_SIZE,loff,ubuf,size); } staticstructfile_operationsgeneral_file_ops= { .open=general_file_open, .read=general_file_read, .write=general_file_write }; staticchardata[4]={0x01,0x05,0x12,0x23}; staticstructdebugfs_blob_wrapperblobData={data,4}; staticint__initdebugfs_demo_init(void) { //1、createdebugfs_demo_dirdirindebugfs debugfs_demo_dir=debugfs_create_dir("debugfs_demo_dir",NULL); if(!debugfs_demo_dir){ pr_err("failedtocreatedebugfsentrydebugfs_demo_dir "); return-1; } //2、creategeneral_fileindebugfs_demo_dir general_file=debugfs_create_file("general_file",0644,debugfs_demo_dir,NULL,&general_file_ops); //3、createu8dataindebugfs u8data_dentry=debugfs_create_u8("u8data",0644,debugfs_demo_dir,&u8data); //4、createx8dataindebugfs x8data_dentry=debugfs_create_x8("x8data",0644,debugfs_demo_dir,&u8data); //5、createboolDataindebugfs bool_dentry=debugfs_create_bool("boolData",0644,debugfs_demo_dir,&boolData); //6、createblobDataindebugfs debugfs_create_blob("blobData",0644,debugfs_demo_dir,&blobData); printk(KERN_INFO"debugfsdemocreatesuccessful "); return0; } staticvoid__exitdebugfs_demo_exit(void) { debugfs_remove_recursive(debugfs_demo_dir); printk(KERN_INFO"debugfs_demo_exit "); } module_init(debugfs_demo_init); module_exit(debugfs_demo_exit); MODULE_AUTHOR("iriczhao"); MODULE_LICENSE("GPL");
在上述代碼中,將在debugfs中創建一個名為debugfs_demo_dir的目錄,并且在該目錄中導出五種類型的數據:
1、通用文件數據:general_file,值默認沒指定
2、以十進制導出數據:u8data,值為90
3、以十六進制導出數據:x8data,值為0x5a
4、布爾類型數據:boolData,值為N
5、blob類型數據:blobData,值為0x01,0x05,0x12,0x23
將上述代碼以模塊方式構建后(模塊名debugfs_demo.ko)拷貝到目標平臺中,使用mount命令查看目前已掛載的文件系統:
發現并沒有掛載debugfs,這時候使用以下命令可以手動掛載debugfs:
mount-tdebugfsdebugfs/sys/kernel/debug/
接著將debugfs_demo.ko加載進內核,完成后將路徑切換進/sys/kernel/debug:
這時候看到期望的debugfs目錄debugfs_demo_dir導出成功,然后切換進該目錄中:
看見了在驅動程序中創建的五個文件,分別查看一下數據:
從輸出結果分析,數據符合驅動程序運行后預期的結果!
審核編輯:劉清
-
寄存器
+關注
關注
31文章
5357瀏覽量
120658 -
Linux系統
+關注
關注
4文章
594瀏覽量
27441 -
十六進制
+關注
關注
2文章
32瀏覽量
37773 -
LINUX內核
+關注
關注
1文章
316瀏覽量
21674 -
gpl
+關注
關注
0文章
26瀏覽量
2183
原文標題:linux內核中的debugfs原來可以這樣玩!
文章出處:【微信號:嵌入式小生,微信公眾號:嵌入式小生】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論