debug fs创建和使用

本文介绍了如何在Linux内核模块中使用debugfs创建文件进行实时调试,以及proc、sysfs和dev文件系统的创建接口、权限模式和操作。通过实例展示了如何在模块初始化时创建debugfs文件,并对比了这些内建文件系统的使用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、说明

在Linux中开发调试内核模块时,可通过创建debug fs,实现运行时跟踪和调试内核信息的功能。

二、创建debug fs

2.1 创建接口

struct dentry *debugfs_create_file(const char *name, mode_t mode, struct dentry *parent, void *data, const struct file_operations *fops);
  • name:要创建的文件的名称。
  • mode:文件的权限模式。
  • parent:父目录的dentry结构的指针,表示要在其中创建新文件的目录。
  • data:可选的指针,用于传递数据给文件的操作函数。
  • fops:指向实现文件操作的file_operations结构的指针。这个结构定义了文件的读取、写入等操作。

通常在内核模块的初始化函数中调用,以在加载模块时创建debug fs方便调试。调用该函数后,将创建一个文件名为name,权限为mode,父目录为parent,并使用fops参数中指定的操作函数来实现文件操作的debug fs。

2.2 权限模式

mode 参数用于指定文件的权限模式,它是一个整数值,通常以八进制表示。常见的权限模式参数包括:

  • S_IRUSR(0400):用户可读
  • S_IWUSR(0200):用户可写
  • S_IXUSR(0100):用户可执行
  • S_IRGRP(0040):组可读
  • S_IWGRP(0020):组可写
  • S_IXGRP(0010):组可执行
  • S_IROTH(0004):其他人可读
  • S_IWOTH(0002):其他人可写
  • S_IXOTH(0001):其他人可执行

这些权限位可以组合使用,例如,若要创建一个用户可读写的文件,组和其他人只能读取的文件,你可以将权限模式设置为 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH,即 0644。

2.3 file_operations

file_operations 是 Linux 内核中用于定义文件操作的结构体,包含了open read write等操作:

struct file_operations {
    struct module *owner;
    loff_t (*llseek) (struct file *, loff_t, int);
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    int (*open) (struct inode *, struct file *);
    int (*release) (struct inode *, struct file *);
    ...
};

三、代码示例

以下是一个在linux内核模块加载时创建debug fs的代码示例:

3.1 实现 file_operations

读接口定义,在此示例中,读接口将字符串通过copy_to_user接口将data字符串拷贝到用户空间缓冲区:

static ssize_t my_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
    char data[] = "Hello, this is debug information!\n";
    size_t data_len = strlen(data);
    ssize_t ret;

    // 确定要读取的数据长度
    size_t read_len = min(count, data_len - *ppos);

    // 检查是否已经读到文件末尾
    if (*ppos >= data_len)
        return 0;

    // 将数据复制到用户空间缓冲区中
    if (copy_to_user(buf, data + *ppos, read_len)) {
        ret = -EFAULT;
        goto out;
    }

    // 更新读取位置
    *ppos += read_len;
    ret = read_len;

out:
    return ret;
}

写接口定义,在此接口中,实现了接受例如0x12345678的16进制数据,并将数据写道reg_addr寄存器中:

static ssize_t my_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
    unsigned long reg_addr = ***;
    char *data;
    u32 reg_val = 0;
    ssize_t ret;

    // 解析用户空间数据
    if (count < sizeof(u32))
        return -EINVAL;
	
	data = kzalloc(count, GFP_KERNEL);
	if (!data)
		return -ENOMEM;

// 方法一, 接收数据后转为u32:
    if (copy_from_user(&data, buf, count))
        return -EFAULT;
    kstrtou32(data, 0, &reg_val);
// 方法二,接收数据并直接转为u32:
	kstrtou32_from_user(buf, count, 0, &reg_val);

    // 写入数据到寄存器
    writel(data, (void __iomem *)reg_addr);

    // 返回写入的字节数
    ret = sizeof(u32) - *ppos;
    *ppos += ret;
    return ret;
}
static const struct file_operations my_fops = {
    .read = my_read,
    .write = my_write,
};

3.2 模块初始化时创建debug fs

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/debugfs.h>

static struct dentry *debugfs_root;

/*
...
file_operations实现代码
...
*/

static int __init my_init(void)
{
    debugfs_root = debugfs_create_dir("my_debugfs", NULL);
    if (!debugfs_root) {
        printk(KERN_ERR "Failed to create debugfs directory\n");
        return -ENOMEM;
    }

	// 在module init接口中,调用debugfs_create_file创建一个调试文件
	// mode: 0666,表示文件用户、组、其它人可读可写
	// my_fops指定文件操作接口
    debugfs_create_file("my_debug_info", 0666, debugfs_root, NULL, &my_fops);

    return 0;
}

static void __exit my_exit(void)
{
    debugfs_remove_recursive(debugfs_root);
}

module_init(my_init);
module_exit(my_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("MODULE Name");

四、其它类似接口

Linux 中还有一些其它类似创建fs的接口,但是创建的位置不同,约定的使用用途也多不相同,但是创建的方法都是相似的:指定创建位置、模式、file_operations 等。

4.1 proc文件系统

  • proc文件系统是一个虚拟文件系统,通常用于显示内核状态、参数和配置;
  • 使用proc_create()系列函数在/proc目录下创建文件;例如:
proc_create("my_proc_file", 0644, NULL, &my_proc_fops);

proc_create接口使用方法与debugfs_create_file类似.

4.2 sysfs 文件系统

  • sysfs文件系统提供了一种机制,用于向用户空间公开内核信息和配置,可以用于设备驱动程序和内核模块之间的通信。
  • 使用sysfs_create_file()函数在/sys目录下创建文件。
struct file *sysfs_create_file(struct kobject *kobj, const struct attribute *attr);
  • kobj:指向包含要创建文件的内核对象的指针。通常,这是表示设备或驱动程序的内核对象。
  • attr:指向 attribute 结构体的指针,描述了要创建的文件的属性,包含了文件的名称、权限模式、读取和写入函数等信息。

例如:

#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/fs.h>
#include <linux/uaccess.h>

#define MAX_DATA_SIZE 1024

static ssize_t my_sysfs_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
    // 在这里实现读取文件的逻辑
    return sprintf(buf, "Hello from sysfs!\n");
}

static ssize_t my_sysfs_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count)
{
    // 在这里实现写入文件的逻辑
    // 这里只是简单的示例,将数据原样写入内核日志
    pr_info("Received data from user: %.*s\n", (int)count, buf);
    return count; // 返回写入的字节数
}

static struct attribute my_attr = {
    .name = "my_sysfs_file",
    .mode = S_IRUGO | S_IWUSR, // 设置文件的权限模式,包括读写权限
    .show = my_sysfs_show,
    .store = my_sysfs_store, // 设置写入函数指针
};

static struct kobject *my_kobj;

static int __init my_init(void)
{
    my_kobj = kobject_create_and_add("my_kobject", NULL);
    if (!my_kobj) {
        pr_err("Failed to create kobject\n");
        return -ENOMEM;
    }

    if (sysfs_create_file(my_kobj, &my_attr)) {
        pr_err("Failed to create sysfs file\n");
        kobject_put(my_kobj);
        return -ENOMEM;
    }

    return 0;
}

static void __exit my_exit(void)
{
    sysfs_remove_file(my_kobj, &my_attr);
    kobject_put(my_kobj);
}

module_init(my_init);
module_exit(my_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("MODLE Name");

4.3 dev 文件系统

  • dev文件系统允许设备驱动程序在/dev目录下创建设备文件,以便用户空间程序可以访问设备。
  • 可以使用register_chrdev()或者cdev_add()等函数注册字符设备或块设备,并在/dev目录下创建相应的设备文件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值