Proc中使用seq_file,数据如何传递

测试代码

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <asm/uaccess.h>

static unsigned int variable;
static struct proc_dir_entry *test_dir, *test_entry;

#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
static int test_proc_read(char *buf, char **start, off_t off, int count,
        int *eof, void *data)
{
    unsigned int *ptr_var = data;
    return sprintf(buf, "test_rw = %u\n", *ptr_var);
}

static int test_proc_write(struct file *file, const char *buffer,
        unsigned long count, void *data)
{
    unsigned int *ptr_var = data;

    *ptr_var = simple_strtoul(buffer, NULL, 10);

    return count;
}
#else
static int test_proc_show(struct seq_file *seq, void *v)
{
    unsigned int *ptr_var = seq->private;
    seq_printf(seq, "%u\n", *ptr_var);
    return 0;
}

static ssize_t test_proc_write(struct file *file, const char __user *buffer,
        size_t count, loff_t *ppos)
{
    struct seq_file *seq = file->private_data;
    unsigned int *ptr_var = seq->private;
    int err;
    char *kbuffer;

        if (!buffer || count > PAGE_SIZE - 1)
                    return -EINVAL;

    kbuffer = (char *)__get_free_page(GFP_KERNEL);
    if (!kbuffer)
        return -ENOMEM;

    err = -EFAULT;
    if (copy_from_user(kbuffer, buffer, count))
        goto out;
    kbuffer[count] = '\0';

    *ptr_var = simple_strtoul(kbuffer, NULL, 10);
    return count;

out:
    free_page((unsigned long)buffer);
    return err;
}

static int test_proc_open(struct inode *inode, struct file *file)
{
    return single_open(file, test_proc_show, PDE_DATA(inode));
}

static const struct file_operations test_proc_fops =
{
    .owner = THIS_MODULE,
    .open = test_proc_open,
    .read = seq_read,
    .write = test_proc_write,
    .llseek = seq_lseek,
    .release = single_release,
};
#endif

static __init int test_proc_init(void)
{
    test_dir = proc_mkdir("test_dir", NULL);
    if (test_dir) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
        test_entry = create_proc_entry("test_rw", 0666, test_dir);
        if (test_entry) {
            test_entry->nlink = 1;
            test_entry->data = &variable;
            test_entry->read_proc = test_proc_read;
            test_entry->write_proc = test_proc_write;
            return 0;
        }
#else
    test_entry = proc_create_data("test_rw",0666, test_dir, &test_proc_fops, &variable);
    if (test_entry)
        return 0;
#endif
    }

    return -ENOMEM;
}
module_init(test_proc_init);

static __exit void test_proc_cleanup(void)
{
    remove_proc_entry("test_rw", test_dir);
    remove_proc_entry("test_dir", NULL);
}
module_exit(test_proc_cleanup);

MODULE_AUTHOR("Barry Song <baohua@kernel.org>");
MODULE_DESCRIPTION("proc exmaple");
MODULE_LICENSE("GPL v2");

代码分析

variable是全局变量,如何在proc操作间传递?


创建节点 proc_create_data

会生成一个proc_dir_entry,->date 用于保存&variable

static unsigned int variable;

static __init int test_proc_init(void)
    test_entry = proc_create_data("test_rw",0666, test_dir, &test_proc_fops, &variable);
        pde = __proc_create(&parent, name, mode, 1);
        pde->proc_fops = proc_fops;
        pde->data      = data;
        proc_register(parent, pde)
        return pde;

结论1

test_entry->date == &variable

open阶段

PDE_DATA(inode)得到文件对应的proc_dir_entry,保存在:file->private_data->private。


file 与 seq_file
static const struct file_operations test_proc_fops =
{
    ...
    .open = test_proc_open,
    ...
};

static int test_proc_open(struct inode *inode, struct file *file)
    single_open(file, test_proc_show, PDE_DATA(inode));
        res = seq_open(file, op);
                p = kmem_cache_zalloc(seq_file_cache, GFP_KERNEL);
                file->private_data = p;
                p->op = op;
        ((struct seq_file *)file->private_data)->private = data;

所以
file->private_date 的值为一个 struct seq_file *p;
p->private = PDE_DATA(inode)
最终, file->private_date->private = PDE_DATA(inode);

PDE_DATA(inode)是什么
struct proc_inode {
    struct pid *pid;
    int fd;
    union proc_op op;
    struct proc_dir_entry *pde;
    struct ctl_table_header *sysctl;
    struct ctl_table *sysctl_entry;
    struct proc_ns ns;
    struct inode vfs_inode;
};

struct proc_inode {
    ...
    struct proc_dir_entry *pde;
    ...
}

/*
 * General functions
 */
static inline struct proc_inode *PROC_I(const struct inode *inode)
{
    return container_of(inode, struct proc_inode, vfs_inode);
}

static inline struct proc_dir_entry *PDE(const struct inode *inode)
{
    return PROC_I(inode)->pde;
}

static inline void *__PDE_DATA(const struct inode *inode)
{
    return PDE(inode)->data;
}

void *PDE_DATA(const struct inode *inode)
{
    return __PDE_DATA(inode);
}
EXPORT_SYMBOL(PDE_DATA);

所以 PDE_DATA(inode)即是 container_of(inode, struct proc_inode, vfs_inode)->pde->data
其中,container_of(inode, struct proc_inode, vfs_inode)->pde 值即为 &test_entry;
那么,container_of(inode, struct proc_inode, vfs_inode)->pde->data 值即为 &test_entry->data;
根据 结论1,可知
结论2

PDE_DATA(inode) = &variable

show/write 阶段,

  • 传参为struct seq_file *seq
static int test_proc_show(struct seq_file *seq, void *v)
{
    unsigned int *ptr_var = seq->private;
  • 传参为struct file *file
static ssize_t test_proc_write(struct file *file, const char __user *buffer,
        size_t count, loff_t *ppos)
{
    struct seq_file *seq = file->private_data;
    unsigned int *ptr_var = seq->private;

总结

proc_create_data(XXX,XXX,XXX,XXX, void *data);
single_open(XXX, XXX, void *data);

如果需要传递参数, single_open的最后一个传参必须为proc_dir_entry->data, 表示方式有:PDE_DATA(inode)inode->datat等;
如果不需要传递数据, proc_create_datasingle_open其中一个void *dataNULL

posted @ 2019-06-08 00:31  gaoyang3513  阅读(1112)  评论(0编辑  收藏  举报