前边说过目录项创建时一般都使用默认的读写函数,本节就来看看默认的文件操作函数的定义。前边已经引用过该定义,此处再次引用:
static const struct file_operations proc_file_operations = {
.llseek = proc_file_lseek,
.read = proc_file_read,
.write = proc_file_write,
};
proc_file_read的实现
从proc文件中读取数据需要经历三个步骤
1.申请内存页用于存储数据
2.调用文件相关的函数填充步骤1申请的内存页
3.把数据从内核空间拷贝到用户空间
很明显,步骤2至关重要,因为这一步正是准备数据。在二.proc数据结构一节我们已经注意到struct proc_dir_entry中提供了两个用于读取数据的函数指针,get_info和read_proc,在proc_file_read中就需要根据不同情况选取二者其一。闲言少叙,上代码说话。一如既往,代码是经过简化的。
static ssize_t proc_file_read(struct file *file, char __user *buf,
size_t nbytes, loff_t *ppos)
{
struct inode * inode = file->f_path.dentry->d_inode;
char *page;
struct proc_dir_entry * dp;
unsigned long long pos;
。。。
dp = PDE(inode);
//此处便是步骤1,获调用函数获取空闲的内存页
if (!(page = (char*) __get_free_page(GFP_TEMPORARY)))
return -ENOMEM;
//步骤2,选择合适的函数
while ((nbytes > 0) && !eof) {
。。。
if (dp->get_info) {
/* Handle old net routines */
n = dp->get_info(page, &start, *ppos, count);
if (n < count)
eof = 1;
} else if (dp->read_proc) {
n = dp->read_proc(page, &start, *ppos,
count, &eof, dp->data);
} else
break;
。。。
//步骤3,将数据从内核空间拷贝到用户空间
n -= copy_to_user(buf, start < page ? page : start, n);
。。。
}
free_page((unsigned long) page);
return retval;
}
这里有一个函数需要说明一下,看代码中红色部分
dp = PDE(inode) ;这个PDE函数的实现如下:
static inline struct proc_dir_entry *PDE(const struct inode *inode)
{
return PROC_I(inode)->pde;
}
这个PROC_I函数是不是很熟悉?呵呵,不错,这不正是第二节中使用container机制获取proc_inode结构使用的那个函数么?现在该明白上边介绍那个函数的用途了吧。
这种阉割后的代码便于理解程序的大致流程,但是省略了很多边界检查,如果需要,请参考内核源代码。
proc_file_write的实现
相比而言proc_file_write的实现相对简单些,这里就不再解释了,直接上代码,明白了proc_file_read函数,这个应该很容易就能读懂。
static ssize_t proc_file_write(struct file *file, const char __user *buffer,size_t count, loff_t *ppos)
{
struct inode *inode = file->f_path.dentry->d_inode;
struct proc_dir_entry * dp;
dp = PDE(inode);
if (!dp->write_proc)
return -EIO;
return dp->write_proc(file, buffer, count, dp->data);
}
proc_file_lseek实现
这个函数的实现就直接在代码中注释了
static loff_t proc_file_lseek(struct file *file, loff_t offset, int orig)
{
loff_t retval = -EINVAL;
switch (orig) {
case 1:
offset += file->f_pos;
/* 原来的位置在文件开始 */
case 0:
/* 如果offset不在文件范围之内,直接返回出错 */
if (offset < 0 || offset > MAX_NON_LFS)
break;
file->f_pos = retval = offset;
}
return retval;
}
到此为止,proc的管理函数就分析完毕了,内核源码中有一个例子Documentation/DocBook/procfs_example.c,就是为了说明这节分析的内容。