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:

1
my_debugfs_root = debugfs_create_dir("mydebug", NULL);

第一个参数是目录的名称,第二个参数用来指定这个目录的上级目录,如果是NULL,则表示是放在debugfs的根目录里。

子目录也是用debugfs_create_dir来实现:

1
sub_dir = debugfs_create_dir("subdir", my_debugfs_root);

建立文件a的代码非常简单:

1
debugfs_create_u8("a", 0644, my_debugfs_root, &a);

这表示文件名为“a”,文件属性是0644,父目录是上面建立的“mydebug”,对应的变量是模块中的a。

Linux内核还提供了其他一些创建debugfs文件的API,请参考本文的附录。

b是一个32-bytes的字符数组,在debugfs里,数组可以用blob wrapper来实现。

1
2
3
4
5
6
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, &b);

这里需要注意的是,blob wrapper定义的数据只能是只读的。在本例中,虽然我们把文件b的权限设定为0644,但实际这个文件还是只读的,如果试图改写这个文件,系统将提示出错。

如果需要对内核数组进行写的动作,blob wrapper就无法满足要求,我们只能通过自己定义文件操作来实现。在这个实作里,可以参考文件c的实现。c和b在模块里对应着同一块字符数组,不同的是,b是只读的,而c通过自定义的文件操作同时实现了读和写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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中,我们要记得释放创建的数据。

1
debugfs_remove_recursive(my_debugfs_root);

debugfs_remove_recursive可以帮我们逐步移除每个分配的dentry,如果您想一个一个手动的移除,也可以直接调用debugfs_remove。

附录:

创建和撤销目录及文件

1
2
3
4
5
6
struct dentry *debugfs_create_dir(const char *name, struct dentry *parent);
struct dentry *debugfs_create_file(const char *name, mode_t mode,
        struct dentry *parent, void *data,
        const struct file_operations *fops);
void debugfs_remove(struct dentry *dentry);
void debugfs_remove_recursive(struct dentry *dentry);

创建单值文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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_u64(const char *name, mode_t mode,
        struct dentry *parent, u64 *value);
 
struct dentry *debugfs_create_x8(const char *name, mode_t mode,
        struct dentry *parent, u8 *value);
struct dentry *debugfs_create_x16(const char *name, mode_t mode,
        struct dentry *parent, u16 *value);
struct dentry *debugfs_create_x32(const char *name, mode_t mode,
        struct dentry *parent, u32 *value);
 
struct dentry *debugfs_create_size_t(const char *name, mode_t mode,
        struct dentry *parent, size_t *value);
struct dentry *debugfs_create_bool(const char *name, mode_t mode,
        struct dentry *parent, u32 *value);

其中,后缀为x8、x16、x32的这三个函数是指debugfs中的数据用十六进制表示。

创建BLOB文件

1
2
3
4
5
6
7
struct debugfs_blob_wrapper {
    void *data;
    unsigned long size;
};
 
struct dentry *debugfs_create_blob(const char *name, mode_t mode,
         struct dentry *parent, struct debugfs_blob_wrapper *blob);

其它

1
2
3
4
5
struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
        struct dentry *new_dir, const char *new_name);
 
struct dentry *debugfs_create_symlink(const char *name,
        struct dentry *parent, const char *target);

作者:wwang 
出处:http://www.cnblogs.com/wwang 
本文采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。

Linux内核里的DebugFS的更多相关文章

  1. [翻译]Linux 内核里的数据结构 —— 基数树

    目录 Linux 内核里的数据结构 -- 基数树 基数树 Radix tree Linux内核基数树API 链接 Linux 内核里的数据结构 -- 基数树 基数树 Radix tree 正如你所知道 ...

  2. Linux 内核里的数据结构:位图(bitmap)

    注: 本文由 LCTT 原创翻译,Linux中国 荣誉推出 Linux 内核中的位数组和位操作 除了不同的基于链式和树的数据结构以外,Linux 内核也为位数组(或称为位图(bitmap))提供了 A ...

  3. Linux 内核里的“智能指针”【转】

    转自:http://blog.jobbole.com/88279/ 众所周知,C/C++语言本身并不支持垃圾回收机制,虽然语言本身具有极高的灵活性,但是当遇到大型的项目时,繁琐的内存管理往往让人痛苦异 ...

  4. Linux 内核里的数据结构:双向链表

    原文:https://blog.csdn.net/qq_33487044/article/details/78827260 双向链表 Linux 内核自己实现了双向链表,可以在 include/lin ...

  5. linux内核里的字符串转换 ,链表操作常用函数(转)

    1.对双向链表的具体操作如下: list_add ———向链表添加一个条目 list_add_tail ———添加一个条目到链表尾部 __list_del_entry ———从链表中删除相应的条目 l ...

  6. Linux 内核里的数据结构:红黑树(rb-tree)

    转自:https://www.cnblogs.com/slgkaifa/p/6780299.html 作为一种数据结构.红黑树可谓不算朴素.由于各种宣传让它过于神奇,网上搜罗了一大堆的关于红黑树的文章 ...

  7. 真正理解红黑树,真正的(Linux内核里大量用到的数据结构,且常被二货问到)

    作为一种数据结构.红黑树可谓不算朴素.由于各种宣传让它过于神奇,网上搜罗了一大堆的关于红黑树的文章,不外乎千篇一律,介绍概念,分析性能,贴上代码,然后给上罪恶的一句话.它最坏情况怎么怎么地...    ...

  8. Linux内核空间-用户空间通信之debugfs

    一.debugfs文件系统简介 debugfs虚拟文件系统是一种内核空间与用户空间的接口,基于libfs库实现,专用于开发人员调试,便于向用户空间导出内核空间数据(当然,反方向也可以).debugfs ...

  9. Linux内核调试的方式以及工具集锦【转】

    转自:https://blog.csdn.net/gatieme/article/details/68948080 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原 ...

随机推荐

  1. QString内部仍采用UTF-16存储数据且不会改变(一共10种不同情况下的编码)

    出处:https://blog.qt.io/cn/2012/05/16/source-code-must-be-utf-8-and-qstring-wants-it/ 但是注意,这只是QT运行(Run ...

  2. Understanding Memory Management(2)

    Understanding Memory Management Memory management is the process of allocating new objects and remov ...

  3. bzoj1497

    这道题让我涨姿势了 对于这类问题,我们称作最大权闭合图问题 就是每个点都有一个点权,要求选择一个点集,其中每个点的指向的点也在点集中,使这样一个点权和最大 对于这种问题,我们添加源点s,汇点t 对于点 ...

  4. BZOJ_3207_花神的嘲讽计划1_(Hash+主席树)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=3207 给出一个长度为\(n\)的串,以及\(m\)个长度为\(k\)的串,求每个长度为\(k\ ...

  5. poj 1167 简单搜索

    这题主要是注意好限定的条件 条件1:每个公交车都至少要到达两次 条件2:公交车相同时间和相同间隔是属于两种车辆 条件3:不同的车可能到达时间相同 上述都是深搜的重要条件: #include<al ...

  6. BrnShop开源网上商城第五讲:自定义视图引擎

    今天这篇博文主要讲解自定义视图引擎,大家都知道在asp.net mvc框架中默认自带一个Razor视图引擎,除此之外我们也可以自定义自己的视图引擎,只需要实现IViewEngine接口,接口定义如下: ...

  7. Android开发视频学习(2)

    S02E05_Android当中的线程 Worker Thread不允许操作UI,只能在Main Thread操作UI S02E06_Handler(一) Handler,Looper,Message ...

  8. [King.yue]关于CSLA框架的一些看法

    CSLA.Net 是一个有帮助的成熟开发框架,但不适于初学者.该框架支持在任何地方.任何时间创建对象,值得我们花时间去学习了解这一框架.CSLA.Net 框架设计的业务对象,支持对完全透明的数据源进行 ...

  9. 华为过滤字符串(java)

    /*通过键盘输入一串小写字母(a~z)组成的字符串.请编写一个字符串过滤程序,若字符串中出现多个相同的字符,将非首次出现的字符过滤掉.比如字符串“abacacde”过滤结果为“abcde”.要求实现函 ...

  10. bzoj 1877 [SDOI2009]晨跑(最小费用最大流)

    Description Elaxia最近迷恋上了空手道,他为自己设定了一套健身计划,比如俯卧撑.仰卧起坐等 等,不过到目前为止,他坚持下来的只有晨跑. 现在给出一张学校附近的地图,这张地图中包含N个十 ...