August 22, 2015 10:13 PM
本文的主要内容整理,转载自: http://edsionte.com/techblog/archives/3030
/proc
下每个文件对应下面一个数据结构
struct proc_dir_entry {
unsigned int low_ino;
unsigned short namelen;
const char *name;
mode_t mode;
nlink_t nlink;
uid_t uid;
gid_t gid;
unsigned long size;
struct inode_operations * proc_iops;
struct file_operations * proc_fops;
get_info_t *get_info;
struct module *owner;
struct proc_dir_entry *next, *parent, *subdir;
void *data;
read_proc_t *read_proc;
write_proc_t *write_proc;
atomic_t count; /* use count */
int deleted; /* delete flag */
};
这个结构比较重要的就是3个钩子函数,分别用于 read
write
info
操作.
typedef int (read_proc_t)(char *page, char **start, off_t off, int count, int *eof, void *data);
page
: 指示用来写入数据的缓冲区;off
和count
: 与read
函数对应的参数相同;start
和eof
: 用于读取大于1个page
数据时实现;typedef int (write_proc_t)(struct file *file, const char __user *buffer, unsigned long count, void *data);
buffer
中有多少数据要被写入;typedef int (get_info_t)(char *, char **, off_t, int);
/proc
文件系统中创建一个目录对应的函数接口如下:
proc_dir_entry *proc_mkdir(const char *,struct proc_dir_entry *);
比如我们使用该函数在/proc下创建一个目录edsionte_procfs。
#define MODULE_NAME "edsionte_procfs"
struct proc_dir_entry *example_dir;
example_dir = proc_mkdir(MODULE_NAME, NULL);
if (example_dir == NULL) {
rv = -ENOMEM;
goto out;
}
在/proc文件系统中创建一个虚拟文件可以使用如下的函数. 这是最直接,包装最少的创建方法.
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent);
proc
文件名;S_IRUGO
,可传入0表示采用系统默认值;proc
目录项,如果为 NULL,表示创建在 /proc 根目录下;create_proc_entry()
完成的任务主要包括:检测 mode
值,分配 proc_dir_entry
结构,注册 proc_dir_entry
.
比如我们通过该函数在/proc/edsionte_procfs
目录下创建一个虚拟文件foo
, 其权限为644
。其中example_dir
指向我们刚创建的目录文件edsionte_procfs
。
struct proc_dir_entry *foo_file;
foo_file = create_proc_entry("foo", 0644, example_dir);
if (foo_file == NULL) {
rv = -ENOMEM;
goto no_foo;
}
当我们需要在/proc
文件系统下创建一个符号链接文件时,可使用如下接口:
struct proc_dir_entry *proc_symlink(const char *name, struct proc_dir_entry *parent, const char *dest);
下面的代码演示了如何通过该函数来对已存在的虚拟文件jiffies创建符号链接文件jiffies_too:
symlink = proc_symlink("jiffies_too", example_dir, "jiffies");
if (symlink == NULL) {
rv = -ENOMEM;
goto no_symlink;
}
既然有创建虚拟文件的函数,必然也就有删除虚拟文件的函数接口:
void remove_proc_entry(const char *name, struct proc_dir_entry *parent);
该函数中的参数name和parent与上述函数的参数意义相同.
在示例程序中,我们在卸载函数中完成上述几个文件的删除工作:
remove_proc_entry("jiffies_too", example_dir);
remove_proc_entry("foo", example_dir);
remove_proc_entry("MODULE_NAME", NULL);
如果只是创建了虚拟文件,那么它并不能被读写。为此,我们必须为每个虚拟文件挂接读写函数,如果该虚拟文件是只读的,那么只需挂载相应的读函数。
正如上面所述,每个虚拟文件对应的proc_dir_entry
结构都有read_proc
和write_proc
两个字段,它们均为函数指针,其各自的类型定义如下:
typedef int (read_proc_t)(char *page, char **start, off_t off, int count, int *eof, void *data);
typedef int (write_proc_t)(struct file *file, const char __user *buffer, unsigned long count, void *data);
在前面也已经介绍过了. 如果要实现对虚拟文件的读写,则需要实现上述两个函数接口。对于我们的示例程序,我们的实现方法如下:
static int proc_read_foobar(char *page, char **start, off_t off, int count, int *eof, void *data)
{
int len;
struct fb_data_t *fb_data = (struct fb_data_t *)data;
//将fb_data的数据写入page
len = sprintf(page, "%s = %s\n", fb_data->name, fb_data->value);
return len;
}
static int proc_write_foobar(struct file *file, const char *buffer, unsigned long count, void *data)
{
int len;
struct fb_data_t *fb_data = (struct fb_data_t *)data;
if (count > FOOBAR_LEN)
len = FOOBAR_LEN;
else
len = count;
//写函数的核心语句,将用户态的buffer写入内核态的value中
if (copy_from_user(fb_data->value, buffer, len))
return -EFAULT;
fb_data->value[len] = '\0';
return len;
}
当用户读我们刚创建的虚拟文件时,该文件对应的read_proc
函数将被调用。该函数将数据写入内核的缓冲区中。上述读函数的例子中,缓冲区即为page
。当用户给虚拟文件写数据时,write_proc
函数将被调用,该函数从缓冲区buffer
中读取count
个字节的数据。