目录
简介:
Linux系统上的/proc目录是一种文件系统,即procfs文件系统。与其它常见的文件系统不同的是,/proc是一种伪文件系统(也即虚拟文件系统),存储的是当前内核运行状态的一系列特殊文件,用户可以通过这些文件查看有关系统硬件及当前正在运行进程的信息,甚至可以通过更改其中某些文件来改变内核的运行状态。
/proc文件系统包含了一些目录(用作组织信息的方式)和虚拟文件。虚拟文件可以向用户呈现内核中的一些信息,也可以用作一种从用户空间向内核发送信息的手段。本文介绍如何在/proc创建读、写接口。
一、/proc下创建目录或文件
proc文件的创建有两种方式:proc_create 和 create_proc_entry。kernel 3.10之前的版本用 create_proc_entry 创建proc文件,新版本create_proc_entry被删除,用proc_create替代create_proc_entry。
1、proc_create
新版本Linux使用 proc_create 创建 /proc 文件
1.1 函数介绍
1.1.1 proc_create()
struct proc_dir_entry *proc_create(const char *name, umode_t mode,
struct proc_dir_entry *parent,
const struct file_operations *proc_fops)
{
return proc_create_data(name, mode, parent, proc_fops, NULL);
}
功能:创建proc文件,并将proc文件和fops操作集绑定
参数:
- *name:创建proc文件的名字
- mode:读写权限,例如:读、写、执行
- *parent:父目录,如果为NULL,会在/proc创建proc文件
- *proc_fops:文件的操作集合,具体可参考例子
返回值:返回当前proc文件的入口结构体
1.1.2 proc_mkdir()
struct proc_dir_entry *proc_mkdir(const char *name,
struct proc_dir_entry *parent)
{
return proc_mkdir_data(name, 0, parent, NULL);
}
功能:在proc下创建目录
参数:
- *name:创建proc目录的名称
- *parent:父目录
返回值:返回当前目录的入口结构体
1.1.3 proc_remove()
void proc_remove(struct proc_dir_entry *de)
{
if (de)
remove_proc_subtree(de->name, de->parent);
}
功能:删除一个已存在的 procfs 文件或目录
1.2 示例
创建proc文件需要实现file_operation结构体,主要实现read和write就可以了。然后通过proc_create将模块注册到内核。/proc目录下就会生成创建的文件。
对该文件进行读写就能实现用户进程与内核的通信。
1.2.1 示例1
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
#include <linux/seq_file.h>
struct proc_dir_entry *hello_proc = NULL;
struct proc_dir_entry *hello_dir = NULL;
/* 定义全局数组保存用户空间写入的数据 */
static char hello_data[20] = "hello world";
static ssize_t hello_proc_open(struct inode *inode, struct file *file)
{
printk("%s run\n", __func__);
return 0;
}
/* 如果使用cat节点会调用该函数
*/
static ssize_t hello_proc_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
int ret = 0;
ret = copy_to_user(buf, hello_data, strlen(hello_data));
if (ret != 0)
{
printk("%s: copy_to_user failed!\n", __func__);
return -EFAULT;
}
printk("%s: %s\n", __func__, buf);
return 0;
}
/* 用户空间使用echo往此节点写入数据 */
static ssize_t hello_proc_write(struct file *file, const char __user *buf, size_t size, loff_t * ppos)
{
int ret;
ret = copy_from_user(hello_data, buf, size);
if (ret != 0)
{
printk("copy_from_user failed!\n");
return -EFAULT;
}
printk("%s: hello_data is %s", __func__, hello_data);
return 0;
}
/* 定义一个file_operations结构体变量 */
static const struct file_operations hello_proc_fops = {
.owner = THIS_MODULE,
.open = hello_proc_open, //用户态调用open函数
.read = hello_proc_read, //使用cat时调用
.write = hello_proc_write, //使用echo时调用
};
static int __init proc_test_init(void)
{
/* 创建 /proc/hello_dir 目录 */
hello_dir = proc_mkdir("hello_dir",NULL);
if(hello_dir == NULL){
printk("%s: proc_mIkdir failed: %s\n", __func__, "hello_dir");
return -EINVAL;
}
/* 创建 /proc/hello_dir/hello_proc 文件,并设置文件操作集 */
hello_proc = proc_create("hello_proc", 0666, hello_dir, &hello_proc_fops);
if (hello_proc == NULL) {
printk("%s: proc_create failed: %s\n", __func__, "hello_proc");
proc_remove(hello_dir); // hello_proc 文件创建失败,删除上一步创建的文件夹 hello_dir
return -EINVAL;
}
return 0;
}
static void __exit proc_test_exit(void)
{
/* 删除文件 */
if(hello_proc)
proc_remove(hello_proc);
/* 删除文件夹 */
if(hello_dir)
proc_remove(hello_dir);
}
module_init(proc_test_init);
module_exit(proc_test_exit);
MODULE_LICENSE("GPL");
用户空间使用echo命令时会调用到 hello_proc_write。使用cat节点会调用 hello_proc_read。
测试结果:
/ # insmod procfs_test.ko # 加载模块
/ # cat /proc/hello_dir/hello_proc # cat 读
hello_proc_open run
hello_proc_read: hello world
/ #
/ # echo "proc_test" > /proc/hello_dir/hello_proc # echo写
/ # cat /proc/hello_dir/hello_proc # cat读
hello_proc_open run
hello_proc_read: proc_test
1.2.2 示例2
procfs通常会和seq_file接口一起使用。
示例功能:获取cmdline中自定义变量,并通过procfs接口使用seq_file方式读取info_value的值
/* 获取cmdline中自定义变量,并通过procfs接口使用seq_file方式读取info_value的值 */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
struct proc_dir_entry *my_proc_entry;
/* 获取cmdline中的值 */
static char cmdline_info_value = 0;
static int __init cmdline_info_setup(char *str)
{
sscanf(str, "%d", &cmdline_info_value);
return 0;
}
__setup("info_value=", cmdline_info_setup);
static int info_value_show(struct seq_file *m, void *v)
{
seq_printf(m, "%d\n", cmdline_info_value);
return 0;
}
static int proc_test_open(struct inode *inode, struct file *filp)
{
return single_open(filp, info_value_show, NULL);
}
static const struct file_operations proc_test_fops = {
.owner = THIS_MODULE,
.open = proc_test_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int __init my_module_init(void)
{
/* 创建 /proc/info_value 文件 */
my_proc_entry = proc_create("info_value", 0660, NULL, &proc_test_fops);
if(!my_proc_entry ){
printk("%s,%s: proc_create failed: %s\n", __func__, "info_value");
return -EIO
}
return 0;
}
static void __exit my_module_exit(void)
{
proc_remove(my_proc_entry);
}
module_init(my_module_init);
module_exit(my_module_exit);
MODULE_LICENSE("GPL");
2、create_proc_entry
Linux 3.10之后的版本create_proc_entry被删除,用proc_create替代create_proc_entry。下面简单介绍下 3.10之前版本如何使用 create_proc_entry。
2.1 函数介绍
2.1.1 创建文件
struct proc_dir_entry *create_proc_entry(constchar *name, mode_t mode, struct proc_dir_entry *parent)
2.1.2 删除文件/目录
void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
2.2 示例
#include <linux/module.h>
#include <linux/sched.h> //jiffies
#include <linux/proc_fs.h>
#include <linux/uaccess.h> //copy_to_user()|copy_from_user()
static char *str = NULL;
//proc文件的读函数
static int my_proc_read(char *buf, char **start, off_t off, int count, int *eof, void *data)
{
int ret = 0;
ret = sprintf(buf, "jiffies = %ld\n", jiffies);
ret += sprintf(buf + ret, "str = %s\n", str);
return ret;
}
//proc文件的写函数
static int my_proc_write(struct file *filp, const char __user *buf, unsigned long count, void *data)
{
//分配临时缓冲区
char *tmp = kzalloc((count+1), GFP_KERNEL);
if (!tmp)
return -ENOMEM;
//将用户态write的字符串拷贝到内核空间
if (copy_from_user(tmp, buf, count)) {
kfree(tmp);
return -EFAULT;
}
//将str的旧空间释放,然后将tmp赋值给str
if (str) {
kfree(str);
}
str = tmp;
return count;
}
struct proc_dir_entry *proc_file = NULL;
static int __init create_proc_init(void)
{
//创建proc文件
proc_file = create_proc_entry("proc_file", 0666, NULL);
if (!proc_file) {
printk("Cannot create /proc/proc_file\n");
return -ENOMEM;
}
//将 /proc/proc_file 文件和读写函数关联
proc_file->read_proc = my_proc_read;
proc_file->write_proc = my_proc_write;
return 0;
}
static void __exit create_proc_exit(void)
{
//删除proc文件
if (proc_file) {
remove_proc_entry("proc_file", NULL);
}
if (str) {
kfree(str);
}
}
module_init(create_proc_init);
module_exit(create_proc_exit);
MODULE_LICENSE("GPL");
参考文档: