当前位置:首页>Linux>Linux debugfs驱动调试

Linux debugfs驱动调试

  • 2026-08-11 17:45:26
Linux debugfs驱动调试

一、debugfs 介绍

1. debugfs是什么?
debugfs(Debug Filesystem)是一个基于内存的虚拟文件系统,专门为内核开发者和驱动开发者设计,用于在内核空间和用户空间之间交换调试信息。

2. 为什么用debugfs?

  • 比 printk 更灵活:无需频繁修改代码、编译、重启。可以动态获取信息。

  • 比 /proc 更纯粹/proc 设计用于进程信息,而 debugfs 专为调试而生,API 更简洁。

  • 比 sysfs 更自由sysfs 有严格的一个值一个文件等规则,debugfs 无此限制,可以输出任意格式的数据(二进制、文本等)。

  • 交互式调试:可以写入文件来触发内核中的特定动作或修改参数。

3. 基本原理

  • 内核在启动时在 /sys/kernel/debug/ 挂载 debugfs 文件系统。

  • 驱动调用 debugfs API 在该目录下创建自己的文件和目录。

  • 用户通过 catechohexdump 等命令读写这些文件,内核中对应的回调函数会被触发。

4. 如何挂载?
如果系统没有自动挂载,可以手动挂载:

sudo mount -t debugfs none /sys/kernel/debug

二、debugfs 核心 API

  1. 创建/删除目录

struct dentry *debugfs_create_dir(const char *name, struct dentry *parent);voiddebugfs_remove(struct dentry *dentry)// 删除文件或目录
2. 创建调试文件
// 支持读写的整数文件struct dentry *debugfs_create_u8/u16/u32/u64(const char *name, umode_t mode, struct dentry *parent, u8 *value);// 类似的 API: debugfs_create_x8/x16/x32/x64 (十六进制显示)// 支持读写的布尔文件struct dentry *debugfs_create_bool(const char *name, umode_t mode, struct dentry *parent, bool *value);
3. 创建自定义文件
struct dentry *debugfs_create_file(const char *name, umode_t mode, struct dentry *parent, void *data, const struct file_operations *fops);
4. 创建符号链接
struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent, const char *target);

三、一个简单的驱动示例

  1. 在 /sys/kernel/debug/example_debugfs/ 下创建目录。

  2. 创建几个调试文件:

    • id:可读写的整数值(文本格式)。

    • counter:只读的递增值(每次读取+1)。

    • data:可读写任意文本数据的文件。

    • flag:可读写的布尔值。

代码:example_debugfs.c

#include<linux/module.h>#include<linux/kernel.h>#include<linux/init.h>#include<linux/debugfs.h>#include<linux/fs.h>#include<linux/uaccess.h>#include<linux/slab.h>#define DRIVER_NAME "example_debugfs"// 驱动私有数据结构struct example_data {    struct dentry *dir;            // debugfs 目录    u32 id;                        // 可读写整数    atomic_t counter;              // 只读计数器    bool flag;                     // 布尔开关    char *data_buffer;             // 自定义数据缓冲区    size_t data_size;};static struct example_data example;// ------------------------------------------------------------// 1. 自定义文件 "data" 的操作函数staticssize_tdata_read(struct file *file, char __user *user_buf,                         size_t count, loff_t *ppos){    ssize_t len;    if (*ppos >= example.data_size)        return 0;    len = min(count, example.data_size - (size_t)*ppos);    if (copy_to_user(user_buf, example.data_buffer + *ppos, len))        return -EFAULT;    *ppos += len;    return len;}staticssize_tdata_write(struct file *file, constchar __user *user_buf,                          size_t count, loff_t *ppos){    char *new_buf;    new_buf = kmalloc(count + 1, GFP_KERNEL);    if (!new_buf)        return -ENOMEM;    if (copy_from_user(new_buf, user_buf, count)) {        kfree(new_buf);        return -EFAULT;    }    new_buf[count] = '\0'// 确保字符串结束    // 替换旧缓冲区    kfree(example.data_buffer);    example.data_buffer = new_buf;    example.data_size = count;    return count;}static const struct file_operations data_fops = {    .owner = THIS_MODULE,    .read = data_read,    .write = data_write,};// ------------------------------------------------------------// 2. 自定义文件 "counter" 的操作函数(只读,每次读自动递增)staticssize_tcounter_read(struct file *file, char __user *user_buf,                            size_t count, loff_t *ppos){    char buf[32];    int val;    ssize_t len;    if (*ppos > 0// 支持多次读        return 0;    val = atomic_inc_return(&example.counter); // 读取并自增    len = snprintf(buf, sizeof(buf), "%d\n", val);    if (copy_to_user(user_buf, buf, len))        return -EFAULT;    *ppos += len;    return len;}static const struct file_operations counter_fops = {    .owner = THIS_MODULE,    .read = counter_read,};// ------------------------------------------------------------// 模块初始化和退出staticint __init example_init(void){    // 1. 创建 debugfs 目录    example.dir = debugfs_create_dir("example_debugfs"NULL);    if (!example.dir) {        printk(KERN_ERR "%s: 创建目录失败\n", DRIVER_NAME);        return -ENOMEM;    }    // 2. 创建各种调试文件    // a) 可读写的32位整数(十进制显示)    debugfs_create_u32("id"0644, example.dir, &example.id);    // b) 可读写的布尔值    debugfs_create_bool("flag"0644, example.dir, &example.flag);    // c) 自定义文件 "counter"(只读,每次读+1)    debugfs_create_file("counter"0444, example.dir, NULL, &counter_fops);    // d) 自定义文件 "data"(可读写任意数据)    debugfs_create_file("data"0644, example.dir, NULL, &data_fops);    // 3. 初始化数据    example.id = 100;    atomic_set(&example.counter, 0);    example.flag = true;    example.data_buffer = kzalloc(1024, GFP_KERNEL);    if (!example.data_buffer) {        debugfs_remove_recursive(example.dir);        return -ENOMEM;    }    strcpy(example.data_buffer, "Hello, debugfs!");    example.data_size = strlen(example.data_buffer);    printk(KERN_INFO "%s: 初始化成功,目录在 /sys/kernel/debug/example_debugfs/\n", DRIVER_NAME);    return 0;}staticvoid __exit example_exit(void){    // 递归删除目录及其下所有文件    debugfs_remove_recursive(example.dir);    kfree(example.data_buffer);    printk(KERN_INFO "%s: 退出\n", DRIVER_NAME);}module_init(example_init);module_exit(example_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("Your Name");MODULE_DESCRIPTION("A simple example of using debugfs");
Makefile
obj-m += example_debugfs.oKERNEL_DIR ?= /lib/modules/$(shell uname -r)/buildPWD := $(shell pwd)all:	$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modulesclean:	$(MAKE) -C $(KERNEL_DIR) M=$(PWD) clean

四、编译、加载和测试

编译&加载驱动 
测试
# 查看目录ls -la /sys/kernel/debug/example_debugfs/# 输出: counter data flag id# 1. 读写 id (整数)cat /sys/kernel/debug/example_debugfs/idecho 200 | sudo tee /sys/kernel/debug/example_debugfs/id# 2. 读写 flag (布尔)cat /sys/kernel/debug/example_debugfs/flagecho N | sudo tee /sys/kernel/debug/example_debugfs/flag # 写 0/N/n 为 false# 3. 读取 counter (每次读会自动+1)cat /sys/kernel/debug/example_debugfs/countercat /sys/kernel/debug/example_debugfs/counter # 再次读取,值会增加# 4. 读写 datacat /sys/kernel/debug/example_debugfs/dataecho "New debug data" | sudo tee /sys/kernel/debug/example_debugfs/data

最新文章

随机文章