当前位置: 首页 > 科技观察

DebugFS

时间:2023-03-13 06:52:04 科技观察

Linux内核中的DebugFS,顾名思义,就是一个用于内核调试的虚拟文件系统。内核开发人员通过debugfs与用户空间交换数据。类似的虚拟文件系统还有procfs和sysfs等,这些虚拟文件系统实际上并不存储在硬盘上,而是在Linux内核运行后建立起来的。通常,最常用的内核调试手段是printk。但printk并非在所有情况下都易于使用。比如打印出来的数据可能太多了,而我们真正关心的数据在大量的输出中并不是那么清晰;或者我们在调试的时候可能需要修改一些内核变量。在这种情况下,printk就无能为力了,如果重新编译内核或者驱动来修改某个值效率太低,那么就需要一个临时文件系统,将我们需要关心的数据映射到用户空间。以前procfs可以达到这个目的,2.6时代新推出的sysfs也可以达到,但是不管是procfs还是sysfs,用它们来达到一定的debug需求似乎都偏离了初衷。比如procfs,它的目的是反映进程的状态信息;而sysfs主要用于Linux设备模型。procfs和sysfs接口都应该保持相对稳定,因为用户模式程序很可能依赖于它们。当然,如果我们只是暂时借用procfs或者sysfs进行调试,在代码发布前删除相关调试代码也是可以的。但如果相关的调试借口要长期存在于内核中,则不宜放在procfs和sysfs中。因此,debugfs应运而生。默认情况下,debugfs将挂载在/sys/kernel/debug目录下。如果它没有自动挂载在你的发行版中,你可以使用以下命令手动完成它:#mount-tdebugfsnone/your/debugfs/dirLinuxkernelisdebugfs提供了一个非常简洁的API。本文接下来将介绍一个示例,示例代码可以从这里下载。这个实现会在debugfs中创建如下目录结构:a对应模块中一个u8类型的变量,subdir下的b和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字节的字符数组,在debugfs中,该数组可以用blobwrapper实现。charhello[32]="Helloworld!\n";structdebugfs_blob_wrapperb;b.data=(void*)hello;b.size=strlen(hello)+1;debugfs_create_blob("b",0644,my_debugfs_root,&b);这里需要注意,blobwrapper定义的数据只能是只读的。在本例中,虽然我们将文件b的权限设置为0644,但实际文件仍然是只读的。如果尝试改写这个文件,系统会提示错误。如果需要写入内核数组的动作,blobwrapper无法满足要求,我们只能通过自己定义文件操作来实现。这个实现可以参考文件c的实现。c和b对应于模块中相同的字符数组。不同的是b是只读的,而c是通过自定义文件操作来实现读写的。staticintc_open(structinode*inode,structfile*filp){filp->private_data=inode->i_private;return0;}staticssize_tc_read(structfile*filp,char__user*buffer,size_tcount,loff_t*ppos){if(*ppos>=32)return0;if(*ppos+count>32)count=32-*ppos;if(copy_to_user(buffer,hello+*ppos,count))return-EFAULT;*ppos+=count;returncount;}staticssize_tc_write(structfile*filp,constchar__user*buffer,size_tcount,loff_t*ppos){if(*ppos>=32)return0;if(*ppos+count>32)count=32-*ppos;if(copy_from_user(hello+*ppos,buffer,count))return-EFAULT;*ppos+=count;returncount;}structfile_operationsc_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。这里,我们也可以换一种写法,在读写函数中使用filp->private_data来引用字符数组hello。至此,已经创建了三个文件和子目录。在module_exit中,我们必须记住释放创建的数据。debugfs_remove_recursive(my_debugfs_root);debugfs_remove_recursive可以帮助我们逐步移除每一个分配