在Linux驱动中使用proc子系统
在Linux驱动中使用proc子系统
背景
proc文件系统是个简单有用的东东:驱动创建一个proc虚拟文件,应用层通过读写该文件,即可实现与内核的交互。
本文适用于3.10以后的内核,v3.10以前的内核调用create_proc_entry
。
参考:
- https://blog.csdn.net/JIANGXIN04211/article/details/50834177
- https://blog.csdn.net/njuitjf/article/details/16940865
创建proc目录
struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent);
描述:创建proc目录。
参数解析:
- name就是要创建的文件夹名称。
- parent是要创建节点的父节点。也就是要在哪个文件夹之下创建新文件夹,需要将那个文件夹的proc_dir_entry传入。
如果是在/proc目录下创建文件夹,则parent为NULL。
例如:
struct proc_dir_entry *mytest_dir = proc_mkdir("mytest", NULL);
创建proc文件
创建方法是调用以下函数:
static inline struct proc_dir_entry *proc_create(const char *name, mode_t mode,
struct proc_dir_entry *parent,
const struct file_operations *proc_fops);
描述:在/proc
中创建文件节点。
参数解析:
- name就是要创建的文件名。
- mode是文件的访问权限,以UGO的模式表示。
- parent,参考
proc_mkdir
- proc_fops:该文件的操作函数。
例如:
#if 0
struct proc_dir_entry *mytest_dir = proc_mkdir("mytest", NULL);
struct proc_dir_entry *mytest_file = proc_create("mytest", 0x0644, mytest_dir, mytest_proc_fops);
#else
struct proc_dir_entry *mytest_file = proc_create("mytest/mytest", 0x0644, NULL, mytest_proc_fops);
#endif
文件操作符
接下来看看mytest_proc_fops的定义。以seq_
和single_
为前缀的函数都是kernel中现成的。
static const struct file_operations mytest_proc_fops = {
.open = mytest_proc_open,
.read = seq_read,
.write = mytest_proc_write,
.llseek = seq_lseek,
.release = single_release,
};
open
在mytest_proc_open
函数中,只需要调用single_open函数,并传入一个show函数即可。
例如:
static int mytest_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, mytest_proc_show, inode->i_private);
}
内核参考文档(Documentation\filesystems\seq_file.txt
)是这么说的:
关于single_
开头的函数,有以下说明:
The extra-simple version
For extremely simple virtual files, there is an even easier interface. A
module can define only the show() function, which should create all the
output that the virtual file will contain. The file's open() method then
calls:
int single_open(struct file *file,
int (*show)(struct seq_file *m, void *p),
void *data);
When output time comes, the show() function will be called once. The data
value given to single_open() can be found in the private field of the
seq_file structure. When using single_open(), the programmer should use
single_release() instead of seq_release() in the file_operations structure
to avoid a memory leak.
data参数
其中第三个参数,也就是single_open的data参数。关于这个参数,内核中有些地方传入的NULL
,有些地方传入的inode->i_private
,也有传入其他值的。
来看看data在single_open函数中如何被使用的:
if (!res)
((struct seq_file *)file->private_data)->private = data;
这与前面的英文说明一致,data是seq_file结构体的private成员。
那么data如何真正被使用的呢?
发现show函数的第一个参数为seq_file类型,在show函数中,可以将seq_file的private成员转换成对应的类型进行使用。
show参数
下面来看看mytest_proc_show函数如何实现。
传递给single_open的show函数指针,将在proc file输出时被调用。
例如,cat /proc/mytest/mytest时,mytest_proc_show函数将会被调用。
假设我们的mytest只是记录了一个标志,内核中会根据该标准执行不同的操作。
来看看mytest_proc_show的实现:
static int task_lifecycle_proc_show(struct seq_file *seq, void *v)
{
seq_puts(seq, mytest_flag ? "true\n" : "false\n");
return 0;
}
write
接下来再看看mytest_proc_write函数的实现。
顾名思义,mytest_proc_write函数会在写mytest文件时被调用。
功能就是记录写入数据到mytest文件。
static ssize_t task_lifecycle_proc_write(struct file *file, const char __user *buffer,
size_t count, loff_t *pos)
{
char mode;
if (count > 0) {
if (get_user(mode, buffer))
return -EFAULT;
mytest_flag = (mode != '0');
}
return count;
}
至此,proc文件创建完毕。
通过读写mytest文件,即可实现对mytest_flag的控制。
例子
驱动
/*************************
* 使用seq_file接口实现可读写的proc文件
* 功能同proc_test02.c
* Author: jx
* Date: 2014-08-08
*************************/
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
static char *str = NULL;
/*********************
* seq_operations->show
* 必须返回0,否则什么也显示不出来
*********************/
static int
my_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, "current kernel time is %ld\n", jiffies);
seq_printf(m, "str is %s\n", str);
return 0;
}
/***********************
* file_operations->open
***********************/
static int
my_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, my_proc_show, NULL);
}
/************************
* file_operations->write
************************/
static ssize_t
my_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *f_pos)
{
char *tmp = kzalloc((count+1), GFP_KERNEL);
if (!tmp)
return -ENOMEM;
if (copy_from_user(tmp,buffer,count)) {
kfree(tmp);
return -EFAULT;
}
kfree(str);
str = tmp;
return count;
}
static struct file_operations my_fops = {
.owner = THIS_MODULE,
.open = my_proc_open,
.release = single_release,
.read = seq_read,
.llseek = seq_lseek,
.write = my_proc_write,
};
static int __init my_init(void)
{
struct proc_dir_entry* file;
//创建proc文件并关联file_operations
file = proc_create("abc_rw", 0644, NULL, &my_fops);
if (!file)
return -ENOMEM;
return 0;
}
static void __exit my_exit(void)
{
remove_proc_entry("abc_rw", NULL);
kfree(str);
}
module_init(my_init);
module_exit(my_exit);
MODULE_AUTHOR("JX");
MODULE_LICENSE("GPL");
Makefile
用于本地机器调试验证使用。
obj-m :=proc.o
# For Ubuntu
KERNEL := /lib/modules/`uname -r`/build
#KERNEL := /lib/modules/`uname -r`/kernel
all:
make -C $(KERNEL) M=`pwd` modules
install:
make -C $(KERNEL) M=`pwd` modules_install
depmod -A
clean:
make -C $(KERNEL) M=`pwd` clean
若在页首无特别声明,本篇文章由 Schips 经过整理后发布。
博客地址:https://www.cnblogs.com/schips/