Linux内核proc_create函数使用指南

8 次阅读

proc_create 是 Linux 内核开发中用于创建 /proc 虚拟文件系统节点的重要接口,广泛应用于内核模块调试、运行状态查看、参数动态配置以及用户态与内核态数据交互。相比传统的 create_proc_entry 方法,proc_create 具有接口更简洁、安全性更高、兼容现代 Linux 内核版本等特点,是当前内核模块开发中推荐使用的方式。

本文将详细介绍 Linux 内核 proc_create 函数的基本原理、函数参数、使用方法、读写操作实现、内核版本兼容问题以及实际开发中的注意事项。

一、Linux proc 文件系统简介

proc 文件系统是一种特殊的虚拟文件系统,它并不直接存储在磁盘中,而是由 Linux 内核动态生成。用户可以通过访问 /proc 目录查看系统运行状态,例如:

Bash
cat /proc/cpuinfo
cat /proc/meminfo
cat /proc/version

这些文件对应的数据都来自内核内部结构。

对于驱动开发和内核模块而言,proc 文件系统提供了一种简单的数据交互方式:

  • 用户空间可以读取内核信息;

  • 用户空间可以向内核传递配置参数;

  • 开发人员可以快速调试内核模块;

  • 无需额外编写 ioctl 接口。

例如,一个网卡驱动可以通过 /proc/my_driver/status 显示当前设备状态,一个安全模块可以通过 /proc/security/config 动态调整参数。

二、proc_create函数作用

proc_create 用于在 /proc 文件系统中创建一个新的文件节点。

函数定义通常位于:

C
#include 

基本形式如下:

C
struct proc_dir_entry *proc_create(
    const char *name,
    umode_t mode,
    struct proc_dir_entry *parent,
    const struct proc_ops *proc_ops
);

调用成功后,内核会创建对应的 proc 文件:

/proc/name

用户可以像访问普通文件一样:

Bash
cat /proc/name
echo data > /proc/name

进行读写操作。

三、proc_create参数详解

1. name 参数

表示创建的 proc 文件名称。

例如:

C
proc_create("hello", 0444, NULL, &proc_fops);

生成:

/proc/hello

注意:

  • 名称不能包含路径分隔符 /;

  • 如果需要创建子目录,需要先使用 proc_mkdir。

例如:

C
/proc/my_module/status

需要:

C
struct proc_dir_entry *dir;

dir = proc_mkdir("my_module", NULL);

proc_create(
    "status",
    0444,
    dir,
    &proc_fops
);

2. mode 参数

表示文件权限,格式与 Linux 文件权限类似。

例如:

C
0666

代表:

用户:读写
用户组:读写
其他:读写

常见设置:

权限说明
0444只读
0644所有者可写,其他只读
0666所有用户可读写
0400仅管理员读取

实际内核开发中通常不会开放过高权限,例如:

C
0644

是比较常见的选择。


3. parent 参数

表示父目录。

如果创建:

/proc/test

通常:

C
parent = NULL

如果创建:

/proc/mydriver/info

则:

C
parent = proc_mkdir("mydriver", NULL);

之后:

C
proc_create(
    "info",
    0444,
    parent,
    &proc_ops
);

4. proc_ops 参数

这是最重要的参数,用于指定文件操作函数。

Linux 5.x 内核以后,推荐使用:

C
struct proc_ops

例如:

C
static const struct proc_ops my_proc_ops =
{
    .proc_read = my_read,
    .proc_write = my_write,
};

其中:

  • proc_read 对应读取;

  • proc_write 对应写入;

  • 还可以定义:

    • proc_open

    • proc_release

    • proc_poll

    • proc_ioctl

等操作。

四、proc_create完整示例

下面实现一个简单 proc 文件:

功能:

  • 读取显示内核变量;

  • 写入修改变量值。

示例代码:

C
#include 
#include 
#include 

#define PROC_NAME "my_proc"

static struct proc_dir_entry *proc_entry;

static int value = 100;


static ssize_t my_proc_read(
    struct file *file,
    char __user *buffer,
    size_t count,
    loff_t *offset)
{
    char data[64];
    int len;


    if (*offset > 0)
        return 0;


    len = sprintf(data, "value=%d
", value);


    if (copy_to_user(buffer, data, len))
        return -EFAULT;


    *offset = len;

    return len;
}


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


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


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


    data[count] = '';


    kstrtoint(data, 10, &value);


    return count;
}


static const struct proc_ops my_proc_ops =
{
    .proc_read = my_proc_read,
    .proc_write = my_proc_write,
};


static int __init my_init(void)
{
    proc_entry = proc_create(
        PROC_NAME,
        0666,
        NULL,
        &my_proc_ops
    );


    if (!proc_entry)
        return -ENOMEM;


    printk("proc created
");

    return 0;
}


static void __exit my_exit(void)
{
    proc_remove(proc_entry);

    printk("proc removed
");
}


module_init(my_init);
module_exit(my_exit);


MODULE_LICENSE("GPL");

加载模块:

Bash
insmod demo.ko

查看:

Bash
cat /proc/my_proc

输出:

value=100

修改:

Bash
echo 200 > /proc/my_proc

再次读取:

Bash
cat /proc/my_proc

结果:

value=200

五、proc_create与create_proc_entry区别

早期 Linux 内核中常见:

C
create_proc_entry()

例如:

C
entry=create_proc_entry(
    "test",
    0666,
    NULL
);

然后手动设置:

C
entry->read_proc=my_read;
entry->write_proc=my_write;

但是这种方式存在问题:

  • 接口设计老旧;

  • 容易产生权限管理问题;

  • 与新版 VFS 结构不兼容。

因此 Linux 3.10 以后逐渐推荐:

C
proc_create()

现代内核模块应该优先使用:

C
proc_create()
+
struct proc_ops

组合。

六、Linux内核版本兼容问题

开发 proc 文件时,需要注意不同内核版本接口变化。

Linux 4.x及以前

通常使用:

C
struct file_operations

例如:

C
static const struct file_operations proc_fops =
{
    .read = my_read,
    .write = my_write,
};

Linux 5.x以后

改为:

C
struct proc_ops

例如:

C
static const struct proc_ops proc_fops =
{
    .proc_read = my_read,
    .proc_write = my_write,
};

如果模块需要同时支持多个版本,可以使用:

C
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)

static const struct proc_ops proc_fops =
{
    .proc_read = my_read,
};

#else

static const struct file_operations proc_fops =
{
    .read = my_read,
};

#endif

这样可以提高代码移植性。

七、proc_create常见问题排查

1. /proc文件没有生成

检查:

Bash
ls /proc

如果不存在,可能原因:

  • 模块初始化函数没有执行;

  • proc_create 返回 NULL;

  • 权限不足;

  • 名称冲突。

建议:

C
if (!entry)
{
    printk("proc_create failed
");
}

查看:

Bash
dmesg

2. 读取内容重复

很多初学者实现 read 时:

C
return len;

但没有处理 offset。

导致:

Bash
cat /proc/test

可能不断输出。

正确方式:

C
if (*offset > 0)
    return 0;

确保只输出一次。


3. copy_to_user失败

内核不能直接访问用户空间地址:

错误:

C
memcpy(buffer,data,len);

正确:

C
copy_to_user(buffer,data,len);

同理:

用户数据进入内核:

C
copy_from_user()

4. 模块卸载后proc节点残留

创建:

C
proc_create()

退出时必须:

C
proc_remove(entry);

否则可能出现:

  • proc目录残留;

  • 访问非法地址;

  • 内核崩溃。

八、proc_create实际应用场景

1. 驱动调试接口

例如:

/proc/device/status

显示:

  • 设备状态;

  • 中断次数;

  • DMA信息。


2. 内核参数动态调整

例如:

/proc/firewall/config

修改:

echo enable > /proc/firewall/config

无需重新加载模块。


3. 系统性能监控

可以输出:

  • 缓存统计;

  • 网络数据;

  • 内存使用情况。


4. 嵌入式Linux开发

在 ARM、RISC-V 等平台中,proc接口经常用于:

  • 外设调试;

  • 驱动状态查看;

  • 现场问题定位。

九、proc_create使用最佳实践

开发 Linux 内核模块时,建议遵循以下原则:

1. 使用proc_create替代旧接口

避免:

C
create_proc_entry()

优先:

C
proc_create()

2. 合理设置权限

不要默认:

C
0666

除非确实需要所有用户修改。


3. 避免复杂逻辑放入read/write

proc接口主要用于:

  • 状态展示;

  • 简单参数控制。

复杂数据交互建议使用:

  • sysfs;

  • netlink;

  • ioctl。


4. 及时释放资源

模块退出:

C
proc_remove()

确保资源完整释放。

十、总结

proc_create 是 Linux 内核模块开发中非常实用的接口,它通过创建 /proc 虚拟文件,实现了用户空间与内核空间之间简单、高效的数据交互。

掌握 proc_create 的关键包括:

  • 理解 proc 文件系统机制;

  • 熟悉函数参数含义;

  • 正确实现 proc_ops;

  • 处理用户空间数据复制;

  • 注意不同 Linux 内核版本兼容;

  • 在模块退出时释放 proc 资源。

对于驱动开发、嵌入式 Linux、内核调试以及系统监控工具开发而言,熟练使用 proc_create 能够显著提升开发效率,也是 Linux 内核工程师需要掌握的重要技能之一。