转载: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:

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通过自定义的文件操作同时实现了读和写。

注:debugfs_create_file()中参数data为私有数据,之后创建文件时会传递给inode->i_private,在file open时可以将inode->i_private传递给file->private_data。

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, 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);

内核交互--debugfs_转的更多相关文章

  1. ifconfig源码分析之与内核交互数据

    <ifconfig源码分析之与内核交互数据>本文档的Copyleft归rosetta所有,使用GPL发布,可以自由拷贝.转载,转载时请保持文档的完整性.参考资料:<Linux设备驱动 ...

  2. Python IO内核交互了解

    注:Unix \ Linux 环境下的network IO   用户空间与内核空间 现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方).操作系 ...

  3. 内核交互--sysfs

    文档介绍:http://lxr.linux.no/linux+v2.6.37/Documentation/filesystems/sysfs.txt The sysfs Filesystem Sysf ...

  4. 内核交互--procfs

    文档介绍:http://lxr.linux.no/linux+v2.6.37/Documentation/filesystems/proc.txt以下内容抄录linux设备驱动开发详解-宋宝华在/pr ...

  5. linux内核交互,设备驱动控制管理接口

    1,ioctl preface--starting point ,format,mount volume,in addition to the above file system -- allows ...

  6. 深入理解Linux网络技术内幕——用户空间与内核空间交互

    概述:     内核空间与用户空间经常需要进行交互.举个例子:当用户空间使用一些配置命令如ifconfig或route时,内核处理程序就要响应这些处理请求.     用户空间与内核有多种交互方式,最常 ...

  7. Linux 用户态与内核态的交互【转载】

    Linux 用户态与内核态的交互  在 Linux 2.4 版以后版本的内核中,几乎全部的中断过程与用户态进程的通信都是使用 netlink 套接字实现的,例如iprote2网络管理工具,它与内核的交 ...

  8. 《Linux内核设计与实现》读书笔记(十八)- 内核调试

    内核调试的难点在于它不能像用户态程序调试那样打断点,随时暂停查看各个变量的状态. 也不能像用户态程序那样崩溃后迅速的重启,恢复初始状态. 用户态程序和内核交互,用户态程序的各种状态,错误等可以由内核来 ...

  9. 戴文的Linux内核专题:02源代码

    转自Linux中国 在下载并解压内核源代码后,用户可以看到许多文件夹和文件.尝试去找一个特定的文件或许是一个挑战.谢天谢地,源代码以一个特定的方式组织的.这使开发者能够轻松找到任何文件或者内核的一部分 ...

随机推荐

  1. 十. 图形界面(GUI)设计11.对话框

    对话框是为了人机对话过程提供交互模式的工具.应用程序通过对话框,或给用户提供信息,或从用户获得信息.对话框是一个临时窗口,可以在其中放置用于得到用户输入的控件.在Swing中,有两个对话框类,它们是J ...

  2. jvm-监视管理控制台-jconsole

    命令: jconsole 作用: jvm进程运行状态的实时.可视化工具 效果: 连接远程jvm进程: 1.首先远程jvm进程,开启了jmx服务: -Dcom.sun.management.jmxrem ...

  3. nginx+php简单配置环境

    首先我的需求是: 1. 需要有PHP的服务.web目录存放在各处. 2. 需要有多个端口. 步骤: 1. 安装nginx php,我的系统是mac 所以安装使用brew, 一键完成... 2. 开启p ...

  4. JavaScript数组api简单说明

    1.一个数组加上另一个(一些)数组,不会修改原数组只会返回新数组 arrayObject.concat(arrayX,arrayX,......,arrayX) 2.把数组按照指定字符串分离,不会修改 ...

  5. Spark Streaming on Kafka解析和安装实战

    本课分2部分讲解: 第一部分,讲解Kafka的概念.架构和用例场景: 第二部分,讲解Kafka的安装和实战. 由于时间关系,今天的课程只讲到如何用官网的例子验证Kafka的安装是否成功.后续课程会接着 ...

  6. HDU2491 Priest John&#39;s Busiest Day

    题目链接 题意: 有n个人要进行乒乓球比赛,每一个人都一个能力值.每一个人出现的次序就是他们住的位置 如今要求进行一场比赛,三个人,裁判的能力值在两个选手之间,住的位置也在两个人的之间 问这样的比赛一 ...

  7. Java8 新的日期和时间API(笔记)

    LocalDate LocalTime Instant duration以及Period 使用LocalDate和LocalTime //2017-03-20 LocalDate date = Loc ...

  8. iOS wkwebview和 uiwebview 内容图片自适应大小

    - (void)webViewDidFinishLoad:(UIWebView *)webView { // 2.都有效果 NSString *js=@"var script = docum ...

  9. Selenium webdriver Java 操作IE浏览器

    V1.0版本:直接新建WebDriver使用 import org.openqa.selenium.WebDriver; import org.openqa.selenium.ie.InternetE ...

  10. 基于Spring+Spring MVC+Mybatis的B2C购物网站

    代码地址如下:http://www.demodashi.com/demo/12935.html 准备工作 当前项目运行的系统环境是MacOS,已经测试可以正常运行,并且之前开发的时候也在Windows ...