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中建立如下的目录结构:
其中,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”,文件属性是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,
这里需要注意的是,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, size_t count, loff_t *ppos) if (*ppos = 32) return 0; if (*ppos + count 32) count = 32 - *ppos; if (copy_from_user(hello + *ppos, buffer, count)) return -EFAULT; *ppos += count; return 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相关文章
- 【ARM-Linux开发】cmem模块/DVSDK2.0
- Linux系统编程-文件操作(一):文件描述符(Linux系统中,一切皆文件)【文件描述符是非负整数,是文件的标识】【打开现存文件或新建文件时,系统(内核)会返回一个文件描述符,用来指定已打开的文件】
- mysql-linux命令登录,退出
- 解决Linux(Loaded plugins: fastestmirror Please use /usr/bin/yum --help)
- Linux内核中的proc文件系统
- C语言在linux内核中do while(0)妙用之法
- linux 消息队列例子
- Linux设备驱动开发详解-Note(5)---Linux 内核及内核编程(1)
- Linux内核出现本地提权漏洞 CVE-2016-5195 2007年以后的版本都可能受到影响
- Linux环境编程:从应用到内核
- 使用socket BPF/Linux内核工程导论——网络:Filter(LSF、BPF、eBPF)
- linux 线程的内核栈是独立的还是共享父进程的?
- linux自动挂载NTFS格式移动硬盘
- linux运维基础之跟我一起学正则表达式(一)
- linux 安装docker
- linux内核概述
- 好记性不如烂笔头——Linux篇
- 数据库与linux中quota的作用
- linux使用ip能ping通,但使用域名却不能访问的解决方法
- Linux 性能优化 - 01、开山初始篇
- 【Hack The Box】Linux练习-- Shibboleth
- Linux内核分析:dup、dup2的实现
- linux yum命令详解
- Visual Studio 2019 + WSL + Linux(Ubuntu)程序配置开发
- 用SD卡下载uboot、linux内核和文件系统
- Linux C 编程 | c程序调用shell脚本
- Linux PM QoS framework(1)_概述和软件架构
- linux内核数据结构 红黑树
- Linux系统内核正式进入5.0版本时代
- 高并发linux内核参数优化