插件窝 干货文章 Linux驱动 | procfs接口创建

Linux驱动 | procfs接口创建

proc static file my 342    来源:    2025-04-14

Linux驱动中procfs接口创建指南

procfs(进程文件系统)是Linux内核提供的一种伪文件系统,允许内核向用户空间导出运行时信息。在驱动开发中,创建procfs接口是一种常见的与用户空间通信的方式。

基本procfs接口创建

1. 包含必要头文件

#include <linux/proc_fs.h>
#include <linux/seq_file.h> // 用于seq_file接口

2. 创建proc文件

// 定义proc文件操作结构体
static const struct proc_ops my_proc_fops = {
    .proc_open = my_proc_open,
    .proc_read = seq_read,
    .proc_lseek = seq_lseek,
    .proc_release = single_release,
};

// 在模块初始化函数中创建proc文件
static int __init my_module_init(void)
{
    proc_create("my_proc_file", 0, NULL, &my_proc_fops);
    return 0;
}

// 在模块退出函数中删除proc文件
static void __exit my_module_exit(void)
{
    remove_proc_entry("my_proc_file", NULL);
}

3. 实现文件操作函数

static int my_proc_show(struct seq_file *m, void *v)
{
    seq_printf(m, "Driver information:\n");
    seq_printf(m, "Version: 1.0\n");
    seq_printf(m, "Status: Active\n");
    return 0;
}

static int my_proc_open(struct inode *inode, struct file *file)
{
    return single_open(file, my_proc_show, NULL);
}

进阶用法

1. 创建proc目录

static struct proc_dir_entry *my_proc_dir;

static int __init my_module_init(void)
{
    // 创建proc目录
    my_proc_dir = proc_mkdir("my_driver", NULL);
    if (!my_proc_dir)
        return -ENOMEM;

    // 在目录下创建文件
    proc_create("status", 0, my_proc_dir, &my_proc_fops);
    return 0;
}

static void __exit my_module_exit(void)
{
    remove_proc_entry("status", my_proc_dir);
    remove_proc_entry("my_driver", NULL);
}

2. 可读写proc文件

static ssize_t my_proc_write(struct file *file, const char __user *buffer,
                             size_t count, loff_t *ppos)
{
    char data[128];

    if (count >= sizeof(data))
        return -EINVAL;

    if (copy_from_user(data, buffer, count))
        return -EFAULT;

    data[count] = '\0';
    // 处理写入的数据
    printk(KERN_INFO "Received: %s\n", data);

    return count;
}

static const struct proc_ops my_rw_proc_fops = {
    .proc_open = my_proc_open,
    .proc_read = seq_read,
    .proc_write = my_proc_write,
    .proc_lseek = seq_lseek,
    .proc_release = single_release,
};

3. 使用seq_file处理大数据

对于大量数据的输出,建议使用seq_file接口:

static int my_seq_show(struct seq_file *m, void *v)
{
    int i;
    for (i = 0; i < 100; i++) {
        seq_printf(m, "Item %d\n", i);
    }
    return 0;
}

static int my_seq_open(struct inode *inode, struct file *file)
{
    return single_open(file, my_seq_show, NULL);
}

static const struct proc_ops my_seq_fops = {
    .proc_open = my_seq_open,
    .proc_read = seq_read,
    .proc_lseek = seq_lseek,
    .proc_release = single_release,
};

最佳实践

  1. 权限控制:通过proc_create_data的mode参数设置文件权限

    proc_create("config", 0644, my_proc_dir, &my_rw_proc_fops);
    
  2. 私有数据:使用proc_create_data传递私有数据

    proc_create_data("status", 0, my_proc_dir, &my_proc_fops, my_private_data);
    
  3. 大文件处理:对于可能很大的输出,总是使用seq_file接口

  4. 安全性

    • 对用户输入进行严格验证
    • 使用copy_from_user/copy_to_user进行用户空间和内核空间的数据交换
    • 避免缓冲区溢出
  5. 资源清理:确保在模块退出时删除所有创建的proc条目

现代替代方案

虽然procfs仍然广泛使用,但Linux社区推荐在新代码中使用sysfs(/sys)或debugfs(/sys/kernel/debug)作为替代,特别是:

  • 配置参数:使用sysfs
  • 调试信息:使用debugfs
  • 运行时状态:procfs或sysfs

procfs更适合于进程相关的信息或传统接口的兼容性维护。

示例完整驱动

#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>

#define PROC_DIR "my_driver"
#define PROC_FILE "status"

static struct proc_dir_entry *my_proc_dir;
static char driver_status[128] = "Initialized";

static int my_proc_show(struct seq_file *m, void *v)
{
    seq_printf(m, "Current status: %s\n", driver_status);
    return 0;
}

static int my_proc_open(struct inode *inode, struct file *file)
{
    return single_open(file, my_proc_show, NULL);
}

static ssize_t my_proc_write(struct file *file, const char __user *buffer,
                            size_t count, loff_t *ppos)
{
    if (count >= sizeof(driver_status))
        return -EINVAL;

    if (copy_from_user(driver_status, buffer, count))
        return -EFAULT;

    driver_status[count] = '\0';
    return count;
}

static const struct proc_ops my_proc_fops = {
    .proc_open = my_proc_open,
    .proc_read = seq_read,
    .proc_write = my_proc_write,
    .proc_lseek = seq_lseek,
    .proc_release = single_release,
};

static int __init my_module_init(void)
{
    my_proc_dir = proc_mkdir(PROC_DIR, NULL);
    if (!my_proc_dir)
        return -ENOMEM;

    if (!proc_create(PROC_FILE, 0644, my_proc_dir, &my_proc_fops)) {
        remove_proc_entry(PROC_DIR, NULL);
        return -ENOMEM;
    }

    printk(KERN_INFO "Procfs interface created\n");
    return 0;
}

static void __exit my_module_exit(void)
{
    remove_proc_entry(PROC_FILE, my_proc_dir);
    remove_proc_entry(PROC_DIR, NULL);
    printk(KERN_INFO "Procfs interface removed\n");
}

module_init(my_module_init);
module_exit(my_module_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Example procfs interface");

这个示例展示了如何创建一个可读写的procfs接口,包含目录创建、权限设置和基本的数据交换功能。