Linux驱动 — 导出proc虚拟文件系统属性信息
proc文件系统
在Linux内核中,procfs(进程文件系统)是一种虚拟的文件系统,它为内核提供了一种向用户空间传递信息的方式,内核用于向用户导出内核信息,如平均负载、内存大小、存储大小等等。
驱动模块可以通过在procfs中创建文件来向用户空间提供信息或接收用户空间的输入。
虚拟文件系统内容都是动态创建的
“/proc”下的绝大多数文件是只读的 ,以显示内核信息为主
Linux系统许多命令是通过分析/proc下的文件来完成:
如ps、top、uptime和free
驱动模块中创建proc文件系统文件和属性
在驱动模块中创建procfs文件系统属性的基本步骤:
- 包含必要的头文件:在驱动模块的源文件中,需要包含linux/proc_fs.h和linux/fs.h头文件。
- 定义文件操作结构体:定义一个file_operations结构体,该结构体包含了处理proc文件的各种方法,如read、write等。
- 实现文件操作函数:根据需要,实现文件操作函数,如read、write等。这些函数将被用户空间的进程用来与proc文件进行交互。
- 创建proc文件:在驱动模块的初始化函数中,使用proc_create或create_proc_entry函数创建proc文件。这些函数需要文件名、权限和一个指向file_operations结构体的指针。
- 注册proc文件:在创建proc文件后,需要将其注册到内核的procfs中。这通常在驱动模块的初始化函数中完成。
- 在proc文件中存储数据:如果需要在proc文件中存储数据,可以在file_operations结构体的read和write函数中实现数据的读写。
- 清理proc文件:在驱动模块的退出函数中,使用remove_proc_entry函数清理proc文件,以防止内存泄漏。
在驱动模块中创建一个proc文件,并通过该文件向用户空间传递一个字符串:
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/fs.h>#define PROCFS_NAME "my_proc_file"static ssize_t proc_read(struct file *file, char __user *usr_buf, size_t count, loff_t *pos);
static ssize_t proc_write(struct file *file, const char __user *usr_buf, size_t count, loff_t *pos);static struct file_operations proc_ops = {.owner = THIS_MODULE,.read = proc_read,.write = proc_write,
};static int __init my_module_init(void) {proc_create(PROCFS_NAME, 0666, NULL, &proc_ops);printk(KERN_INFO "/proc/%s created\n", PROCFS_NAME);return 0;
}static void __exit my_module_exit(void) {remove_proc_entry(PROCFS_NAME, NULL);printk(KERN_INFO "/proc/%s removed\n", PROCFS_NAME);
}static ssize_t proc_read(struct file *file, char __user *usr_buf, size_t count, loff_t *pos) {int rv = 0;char *msg = "Hello from kernel space!\n";if (*pos >= strlen(msg))return 0;if (count > strlen(msg) - *pos)count = strlen(msg) - *pos;if (copy_to_user(usr_buf, msg + *pos, count))return -EFAULT;*pos += count;rv = count;return rv;
}static ssize_t proc_write(struct file *file, const char __user *usr_buf, size_t count, loff_t *pos) {// Implement write functionality if neededreturn -EINVAL;
}module_init(my_module_init);
module_exit(my_module_exit);MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A simple example of creating a proc file");
MODULE_AUTHOR("Your Name");
[RSU7032: customer]# insmod proc_file.ko
[RSU7032: customer]# lsmod
proc_file 1168 0 - Live 0xbf002000 (O)
sdxxx 543084 1 - Live 0xbf07c000 (O)
mlan 432260 1 sdxxx, Live 0xbf004000 (O)
rsu7012_iomux 2481 0 - Live 0xbf000000 (O)
[RSU7032: customer]# cat /proc/my_proc_file
Hello from kernel space!
在这个示例中,我们创建了一个名为my_proc_file的proc文件,用户可以通过读取这个文件来获取内核空间的消息。在proc_read函数中,我们实现了将消息从内核空间复制到用户空间的功能。在proc_write函数中,我们简单地返回了一个错误代码,表示不支持写入操作。
实际使用时可能需要根据具体需求进行修改和扩展。
从Linux 3.10开始,推荐使用proc_create_data和remove_proc_entry函数来创建和删除proc文件,因为proc_create和create_proc_entry函数已被标记为过时。
从Linux 3.10开始,proc_create和create_proc_entry函数已被标记为过时,推荐使用proc_create_data和remove_proc_entry函数来创建和删除proc文件。以下是使用proc_create_data的示例
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/fs.h>#define PROCFS_NAME "my_proc_file"static ssize_t proc_read(struct file *file, char __user *usr_buf, size_t count, loff_t *pos);
static ssize_t proc_write(struct file *file, const char __user *usr_buf, size_t count, loff_t *pos);static struct file_operations proc_ops = {.owner = THIS_MODULE,.read = proc_read,.write = proc_write,
};static int __init my_module_init(void) {proc_create_data(PROCFS_NAME, 0666, NULL, &proc_ops, NULL);printk(KERN_INFO "/proc/%s created\n", PROCFS_NAME);return 0;
}static void __exit my_module_exit(void) {remove_proc_entry(PROCFS_NAME, NULL);printk(KERN_INFO "/proc/%s removed\n", PROCFS_NAME);
}static ssize_t proc_read(struct file *file, char __user *usr_buf, size_t count, loff_t *pos) {int rv = 0;char *msg = "Hello from kernel space!\n";if (*pos >= strlen(msg))return 0;if (count > strlen(msg) - *pos)count = strlen(msg) - *pos;if (copy_to_user(usr_buf, msg + *pos, count))return -EFAULT;*pos += count;rv = count;return rv;
}static ssize_t proc_write(struct file *file, const char __user *usr_buf, size_t count, loff_t *pos) {// Implement write functionality if neededreturn -EINVAL;
}module_init(my_module_init);
module_exit(my_module_exit);MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A simple example of creating a proc file");
MODULE_AUTHOR("Your Name");
创建proc文件系统属性,实现读取
在 Linux 4.0 内核驱动中,虽然官方已不鼓励继续向 /proc 里添加新条目(官方推荐用 /sys 或 debugfs),但如果出于兼容性目的仍想在 /proc 里创建可读属性,可按下面步骤完成。核心思路是:
- 在模块初始化时创建目录及文件(proc_mkdir / proc_create)。
- 实现一个 read_proc(老接口)或 .read(seq_file 新接口)函数,把内核数据拷贝给用户。
- 清理时把目录/文件删掉即可。
下面给出两种写法,任选其一即可编译通过。
3.10 以后内核主推的 seq_file 接口
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>static int my_val = 42; /* 要暴露给用户的值 *//* seq_show 会被多次调用,直到返回 0 */
static int my_proc_show(struct seq_file *m, void *v)
{seq_printf(m, "%d\n", my_val);return 0;
}static int my_proc_open(struct inode *inode, struct file *file)
{/* 单 open 接口,内部已经帮你完成所有 seq_file 初始化 */return single_open(file, my_proc_show, NULL);
}static const struct file_operations my_proc_fops = {.owner = THIS_MODULE,.open = my_proc_open,.read = seq_read,.llseek = seq_lseek,.release = single_release,
};static struct proc_dir_entry *my_dir;
static struct proc_dir_entry *my_file;static int __init my_init(void)
{/* 1. 创建 /proc/my_drv 目录 */my_dir = proc_mkdir("my_drv", NULL);if (!my_dir)return -ENOMEM;/* 2. 在目录里创建 read-only 文件 */my_file = proc_create("val", 0444, my_dir, &my_proc_fops);if (!my_file) {proc_remove(my_dir);return -ENOMEM;}return 0;
}static void __exit my_exit(void)
{proc_remove(my_file); /* 也可直接 proc_remove(my_dir) 把目录一起删 */proc_remove(my_dir);
}module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
Mafile:
KERNEL_DIR = /home/xgj/workspace/project/rsu7012/kernel/linux-3.10.79export PATH := $(PATH):/home/xgj/workspace/project/rsu7012/toolchains/arm-eabi/binARCH=arm
CROSS_COMPILE=arm-eabi-
export ARCH CROSS_COMPILEobj-m += proc_attr.obuild: proc_attr proc_attr:$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules.PHONY:clean
clean:$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean
测试:
[RSU7032: customer]# insmod proc_attr.ko
[RSU7032: customer]# lsmod
proc_attr 1008 0 - Live 0xbf071000 (O)
proc_file 1168 0 - Live 0xbf002000 (O)
sdxxx 543084 1 - Live 0xbf07c000 (O)
mlan 432260 1 sdxxx, Live 0xbf004000 (O)
rsu7012_iomux 2481 0 - Live 0xbf000000 (O)
[RSU7032: customer]# cat /proc/m
meminfo misc modules mounts mwlan/ my_drv/ my_proc_file
[RSU7032: customer]# cat /proc/my_drv/val
42