活动地址:CSDN21天学习挑战赛
学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。各位小伙伴,如果您:
想系统/深入学习某技术知识点…
一个人摸索学习很难坚持,想组团高效学习…
想写博客但无从下手,急需写作干货注入能量…
热爱写作,愿意让自己成为更好的人…
debugfs虚拟文件系统是一种内核空间与用户空间的接口,基于libfs库实现,专用于开发人员调试,便于向用户空间导出内核空间数据(当然,反方向也可以)。debugfs在linux内核版本2.6.10引入,作者是Greg Kroah-Hartman。
与procfs和sysfs不同,前者主要提供进程信息(当然后来又加入设备、内存、网络等信息,比较杂乱),后者主要提供设备信息,且有一个文件提供一个值的“规则”,是Linux通用设备模型的影射。debugfs没有类似的限制,开发者可以放入任何信息。
debugfs的API接口定义在kernel/include/linux/debugfs.h,要使用debugfs提供的api必须包含头文件#include 且使能编译宏CONFIG_DEBUG_FS常用的如下:
//kernel/include/linux/debugfs.h
/* 在指定的debugfs中创建一个目录,如果parent为空,则创建在在debugfs的根目录(/sys/kernel/debug) */
struct dentry *debugfs_create_dir(const char *name, struct dentry *parent);
/* 在debugfs中创建文件,需要实现文件的操作方法,如读写 */
struct dentry *debugfs_create_file(const char *name, umode_t mode,
struct dentry *parent, void *data,
const struct file_operations *fops);
/* 删除debugfs中的文件 */
void debugfs_remove(struct dentry *dentry);
/* 删除debugfs中的目录 */
void debugfs_remove_recursive(struct dentry *dentry);
/* 注意:因为在debugfs中创建的文件和目录在模块退出时,并不会自动删除,所以存在了上述API */
/* 通过上面的API可以知道我们可以在debugfs中实现文件的创建删除,目录的创建删除,针对文件创建的时候还需要实现文件的操作方法,这是在某些时候比较麻烦的,必须我不需要有啥操作的,仅仅只是需要依靠文件导出而已,不需要啥操作,对此,debugfs提供了如下API */
/* 将创建的文件绑定到u8变量 */
struct dentry *debugfs_create_u8(const char *name, umode_t mode,
struct dentry *parent, u8 *value);
/* 将创建的文件绑定到u16变量 */
struct dentry *debugfs_create_u16(const char *name, umode_t mode,
struct dentry *parent, u16 *value);
/* 将创建的文件绑定到u32变量 */
struct dentry *debugfs_create_u32(const char *name, umode_t mode,
struct dentry *parent, u32 *value);
/* 将创建的文件绑定到u64变量 */
struct dentry *debugfs_create_u64(const char *name, umode_t mode,
struct dentry *parent, u64 *value);
/* 关于debugfs的API这里列举的只是九牛一毛,剩下的感谢的朋友可以自行阅读该文件 */




debugfs全称Debug Filesystem,由宏CONFIG_DEBUG_FS控制。该宏使能编译后,系统启动后,内核自动挂载到默认目录/sys/kernel/debug,如果系统启动后没有自动挂载可使用该命令手动挂载,mount -t debugfs none /sys/kernel/debug,使能如下:


#include
#include
#include
static struct dentry *dir = NULL;
static unsigned int debugfs_hello;
static u32 sum = 0;
static int add_write(void *data, u64 value)
{
sum += value;
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(add_ops, NULL, add_write, "%llu\n");
static __init int hello_init(void)
{
struct dentry *tmp_dir = NULL;
/* create /sys/kernel/debug/debugfs_hello/ directory */
dir = debugfs_create_dir("debugfs_hello", NULL);
if (!dir) {
printk(KERN_ALERT "debugfs_create_dir failed\n");
return -1;
}
/* create /sys/kernel/debug/debugfs_hello/hello value, mode: rw*/
tmp_dir = debugfs_create_u32("hello", 00666, dir, &debugfs_hello);
if (!tmp_dir) {
printk(KERN_ALERT "debugfs_create_u32 failed\n");
return -1;
}
/* create /sys/kernel/debug/debugfs_hello/add value, mode: w*/
tmp_dir = debugfs_create_file("add", 0222, dir, NULL, &add_ops);
if (!tmp_dir) {
printk(KERN_ALERT "debugfs_create_file failed\n");
return -1;
}
/* create /sys/kernel/debug/debugfs_hello/sum value, mode: r*/
tmp_dir = debugfs_create_u32("sum", 0444, dir, &sum);
if (!tmp_dir) {
printk(KERN_ALERT "debugfs_create_u32 failed\n");
return -1;
}
return 0;
}
static void __exit hello_exit(void)
{
printk(KERN_INFO "Exit debugfs_hello module\n");
debugfs_remove_recursive(dir);
dir = NULL;
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("Debugfs hello example");
经过上面的驱动加载,可以在debugfs驱动的根目录找到我们的驱动debugfs_hello,如下:

现在可以来测试下该驱动实现的功能,它所实现的就是一个加法驱动,主要体现在三个文件,如下图所示,且三个文件的读写权限功能各不相同,接下来通过后面一系列操作来阐述。

查看三个文件的值,可以看到这里hello和sum的初始值默认为0,而add是不可读的,它没有读权限,为什么?因为它的实现目的是加法运算,不是用来存储展示给人看的值,因此不需要读权限。

现在先看hello文件,尝试往hello中写入数据11,写入成功查看,11成功写入到hello文件,接着继续查看三个文件的值,这里发现只echo 11 > hello 只改变了hello的值,为什么?因为hello文件和其他两个文件没有什么关系,它仅仅是个单纯的存储值的文件。

接下来看sum文件,尝试往sum中写入值,我们发现无法写入,没有写权限,为什么?顾名思义sum是最终的和结果,它应该只具备读权限。

接下来看add文件,尝试往add文件中写入111,发现写入成功,这里侧面验证了sum文件的功能和特性(可读不可写),继续读取三个值,发现sum由0变为111了,这里也可以看出往add中输入的值,通过add操作将最终值给到了sum。因此最终得出了sum和add是有联系的两个文件,通过往add写值,最终结果add到sum文件,由sum文件保存和显示。

扩展:在不阅读源码的情况下,我们可以猜测add是持续add还是单次add呢?我们接着往add中写入111看看什么现象。

这里很明显的说明add是持续add,即累计add,最终结果由sum显示。
参考:Debugfs