proc源码解析(六)--proc文件读写函数

时间:2022-12-17 00:10:32
前边说过目录项创建时一般都使用默认的读写函数,本节就来看看默认的文件操作函数的定义。前边已经引用过该定义,此处再次引用:
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,就是为了说明这节分析的内容。