zoukankan      html  css  js  c++  java
  • 在Linux驱动中使用proc子系统

    在Linux驱动中使用proc子系统

    背景

    proc文件系统是个简单有用的东东:驱动创建一个proc虚拟文件,应用层通过读写该文件,即可实现与内核的交互。

    本文适用于3.10以后的内核,v3.10以前的内核调用create_proc_entry

    参考:

    创建proc目录

    struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent);
    

    描述:创建proc目录。

    参数解析:

    • name就是要创建的文件夹名称。
    • parent是要创建节点的父节点。也就是要在哪个文件夹之下创建新文件夹,需要将那个文件夹的proc_dir_entry传入。

    如果是在/proc目录下创建文件夹,则parent为NULL。

    例如:

    struct proc_dir_entry *mytest_dir = proc_mkdir("mytest", NULL);
    

    创建proc文件

    创建方法是调用以下函数:

    static inline struct proc_dir_entry *proc_create(const char *name, mode_t mode,
                                                     struct proc_dir_entry *parent, 
                                                     const struct file_operations *proc_fops);
    

    描述:/proc中创建文件节点。

    参数解析:

    • name就是要创建的文件名。
    • mode是文件的访问权限,以UGO的模式表示。
    • parent,参考proc_mkdir
    • proc_fops:该文件的操作函数。

    例如:

    #if 0
    struct proc_dir_entry *mytest_dir = proc_mkdir("mytest", NULL);
    struct proc_dir_entry *mytest_file = proc_create("mytest", 0x0644, mytest_dir, mytest_proc_fops);
    #else
    struct proc_dir_entry *mytest_file = proc_create("mytest/mytest", 0x0644, NULL, mytest_proc_fops);
    #endif
    

    文件操作符

    接下来看看mytest_proc_fops的定义。以seq_single_为前缀的函数都是kernel中现成的。

    static const struct file_operations mytest_proc_fops = {
        .open  = mytest_proc_open,
        .read  = seq_read,
        .write  = mytest_proc_write,
        .llseek  = seq_lseek,
        .release = single_release,
    };
    

    open

    mytest_proc_open函数中,只需要调用single_open函数,并传入一个show函数即可。

    例如:

    static int mytest_proc_open(struct inode *inode, struct file *file)
    {
        return single_open(file, mytest_proc_show, inode->i_private);
    }
    

    内核参考文档(Documentationfilesystemsseq_file.txt)是这么说的:

    关于single_开头的函数,有以下说明:

    The extra-simple version
    
    For extremely simple virtual files, there is an even easier interface.  A
    module can define only the show() function, which should create all the
    output that the virtual file will contain. The file's open() method then
    calls:
    
     int single_open(struct file *file,
                     int (*show)(struct seq_file *m, void *p),
                     void *data);
    
    When output time comes, the show() function will be called once. The data
    value given to single_open() can be found in the private field of the
    seq_file structure. When using single_open(), the programmer should use
    single_release() instead of seq_release() in the file_operations structure
    to avoid a memory leak.
    
    data参数

    其中第三个参数,也就是single_open的data参数。关于这个参数,内核中有些地方传入的NULL,有些地方传入的inode->i_private,也有传入其他值的。

    来看看data在single_open函数中如何被使用的:

    if (!res)
        ((struct seq_file *)file->private_data)->private = data;
    

    这与前面的英文说明一致,data是seq_file结构体的private成员。

    那么data如何真正被使用的呢?

    发现show函数的第一个参数为seq_file类型,在show函数中,可以将seq_file的private成员转换成对应的类型进行使用。

    show参数

    下面来看看mytest_proc_show函数如何实现。

    传递给single_open的show函数指针,将在proc file输出时被调用。

    例如,cat /proc/mytest/mytest时,mytest_proc_show函数将会被调用。

    假设我们的mytest只是记录了一个标志,内核中会根据该标准执行不同的操作。

    来看看mytest_proc_show的实现:

    static int task_lifecycle_proc_show(struct seq_file *seq, void *v)
    {
        seq_puts(seq, mytest_flag ? "true
    " : "false
    ");
    
        return 0;        
    }
    

    write

    接下来再看看mytest_proc_write函数的实现。

    顾名思义,mytest_proc_write函数会在写mytest文件时被调用。

    功能就是记录写入数据到mytest文件。

    static ssize_t task_lifecycle_proc_write(struct file *file, const char __user *buffer,
                                             size_t count, loff_t *pos)
    {
        char mode;
    
        if (count > 0) {
            if (get_user(mode, buffer))
                return -EFAULT;
    
            mytest_flag = (mode != '0');
        }
    
        return count;
    }
    

    至此,proc文件创建完毕。

    通过读写mytest文件,即可实现对mytest_flag的控制。

    例子

    驱动

    /*************************
     * 使用seq_file接口实现可读写的proc文件
     * 功能同proc_test02.c
     * Author: jx
     * Date: 2014-08-08
     *************************/
    #include <linux/module.h>
    #include <linux/sched.h>
    #include <linux/fs.h>
    #include <linux/proc_fs.h>
    #include <linux/seq_file.h>
    #include <linux/uaccess.h>
    #include <linux/slab.h>
    
    static char *str = NULL;
    
    /*********************
     * seq_operations->show
     * 必须返回0,否则什么也显示不出来
     *********************/
    static int 
        my_proc_show(struct seq_file *m, void *v)
    {
        seq_printf(m, "current kernel time is %ld
    ", jiffies);
        seq_printf(m, "str is %s
    ", str);
    
        return 0;
    }
    
    /***********************
     * file_operations->open
     ***********************/
    static int 
        my_proc_open(struct inode *inode, struct file *file)
    {
        return single_open(file, my_proc_show, NULL);
    }
    
    /************************
     * file_operations->write
     ************************/
    static ssize_t 
        my_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *f_pos)
    {
        char *tmp = kzalloc((count+1), GFP_KERNEL);
        if (!tmp)
            return -ENOMEM;
    
        if (copy_from_user(tmp,buffer,count)) {
            kfree(tmp);
            return -EFAULT;
        }
    
        kfree(str);
        str = tmp;
    
        return count;
    }
    
    static struct file_operations my_fops = {
        .owner	= THIS_MODULE,
        .open	= my_proc_open,
        .release = single_release,
        .read	= seq_read,
        .llseek	= seq_lseek,
        .write 	= my_proc_write,
    };
    
    static int __init my_init(void)
    {
        struct proc_dir_entry* file;
    
        //创建proc文件并关联file_operations
        file = proc_create("abc_rw", 0644, NULL, &my_fops);
        if (!file)
            return -ENOMEM;
    
        return 0;
    }
    
    static void __exit my_exit(void)
    {
        remove_proc_entry("abc_rw", NULL);
        kfree(str);
    }
    
    module_init(my_init);
    module_exit(my_exit);
    
    MODULE_AUTHOR("JX");
    MODULE_LICENSE("GPL");
    

    Makefile

    用于本地机器调试验证使用。

    obj-m :=proc.o
    # For Ubuntu
    KERNEL := /lib/modules/`uname -r`/build
    #KERNEL := /lib/modules/`uname -r`/kernel
     
    all:
        make -C $(KERNEL) M=`pwd` modules
    install:
        make -C $(KERNEL) M=`pwd` modules_install
        depmod -A
    clean:
        make -C $(KERNEL) M=`pwd` clean
    
    如果说我的文章对你有用,只不过是我站在巨人的肩膀上再继续努力罢了。
    若在页首无特别声明,本篇文章由 Schips 经过整理后发布。
    博客地址:https://www.cnblogs.com/schips/
  • 相关阅读:
    7-3色彩平衡
    Flex 布局
    JS获取当前周
    虚树学习笔记
    当然,perl等脚本服务器是一般默认安装了,你入侵了一台主机,总不能先装配 Java 环境然后再开干吧?
    还有这种书,程序开发心理学(豆瓣)
    Codeforces Round #143 (Div. 2)
    Codeforces Round #142 (Div. 2)
    Codeforces Round #141 (Div. 2)
    2018-2019-1 20189221 书籍速读 第 1 周
  • 原文地址:https://www.cnblogs.com/schips/p/using_proc_ss_in_linux_driver.html
Copyright © 2011-2022 走看看