美文网首页android驱动开发
如何使用内核API函数 proc_create?

如何使用内核API函数 proc_create?

作者: _invincible_ | 来源:发表于2020-02-17 02:25 被阅读0次

    1. 问题来源

    看到一个null pointer dereference的demo使用了这个函数。

    2. 概述

    Proc文件系统
    Proc File System是一个虚拟的文件系统,可以理解为内核对用户开放的接口,让内核和用户进程进行数据交换 (读取内核进程的数据,修改内核参数等):

    cat /proc/cpuinfo
    

    Creating a new Proc file
    To create a proc file system we need to implement a simple interface – file_operation.We can implement more than 20 functions but the common operations are read, write.

    To register the interface use the function proc_create.

    要创建一个Proc file需要实现file_operation结构体,主要实现read和write就可以了。然后通过proc_create来注册。将模块注册到内核后,就能在/proc/目录找到我们的文件。
    对该文件进行读写就能实现用户进程与内核的通信。

    3. 示例

    mydev.c:

    #include <linux/module.h>
    #include <linux/moduleparam.h>
    #include <linux/init.h>
    #include <linux/kernel.h>   
    #include <linux/proc_fs.h>
    #include <asm/uaccess.h>
    #define BUFSIZE  100
    
    
    MODULE_LICENSE("Dual BSD/GPL");
    MODULE_AUTHOR("Liran B.H");
    
    static int irq=20;
    module_param(irq,int,0660);
    
    static int mode=1;
    module_param(mode,int,0660);
    
    static struct proc_dir_entry *ent;
    
    static ssize_t mywrite(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) 
    {
        int num,c,i,m;
        char buf[BUFSIZE];
        if(*ppos > 0 || count > BUFSIZE)
            return -EFAULT;
        if(copy_from_user(buf, ubuf, count))
            return -EFAULT;
        num = sscanf(buf,"%d %d",&i,&m);
        if(num != 2)
            return -EFAULT;
        irq = i; 
        mode = m;
        c = strlen(buf);
        *ppos = c;
        return c;
    }
    
    static ssize_t myread(struct file *file, char __user *ubuf,size_t count, loff_t *ppos) 
    {
        char buf[BUFSIZE];
        int len=0;
        if(*ppos > 0 || count < BUFSIZE)
            return 0;
        len += sprintf(buf,"irq = %d\n",irq);
        len += sprintf(buf + len,"mode = %d\n",mode);
        
        if(copy_to_user(ubuf,buf,len))
            return -EFAULT;
        *ppos = len;
        return len;
    }
    
    static struct file_operations myops = 
    {
        .owner = THIS_MODULE,
        .read = myread,
        .write = mywrite,
    };
    
    static int simple_init(void)
    {
        ent=proc_create("mydev",0666,NULL,&myops);
        printk(KERN_ALERT "hello...\n");
        return 0;
    }
    
    static void simple_cleanup(void)
    {
        proc_remove(ent);
        printk(KERN_WARNING "bye ...\n");
    }
    
    module_init(simple_init);
    module_exit(simple_cleanup);
    

    Makefile:

    obj-m += mydev.o
    
    modules:
        $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
        
    clean:
        $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
    

    output:

    invincible@ubuntu:~/Desktop/my_mods/mydev$ make
    make -C /lib/modules/4.4.0-112-generic/build M=/home/invincible/Desktop/my_mods/mydev modules
    make[1]: Entering directory '/usr/src/linux-headers-4.4.0-112-generic'
      CC [M]  /home/invincible/Desktop/my_mods/mydev/mydev.o
      Building modules, stage 2.
      MODPOST 1 modules
      CC      /home/invincible/Desktop/my_mods/mydev/mydev.mod.o
      LD [M]  /home/invincible/Desktop/my_mods/mydev/mydev.ko
    make[1]: Leaving directory '/usr/src/linux-headers-4.4.0-112-generic'
    invincible@ubuntu:~/Desktop/my_mods/mydev$ insmod mydev.ko 
    insmod: ERROR: could not insert module mydev.ko: Operation not permitted
    invincible@ubuntu:~/Desktop/my_mods/mydev$ sudo insmod mydev.ko 
    invincible@ubuntu:~/Desktop/my_mods/mydev$ ls /proc/my*
    /proc/mydev
    invincible@ubuntu:~/Desktop/my_mods/mydev$ echo "32 6" > /proc/mydev
    invincible@ubuntu:~/Desktop/my_mods/mydev$ cat /proc/mydev 
    irq = 32
    mode = 6
    invincible@ubuntu:~/Desktop/my_mods/mydev$ 
    

    user_app.c:

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
     
    void main(void)
    {
        char buf[100];
        int fd = open("/proc/mydev", O_RDWR);
        read(fd, buf, 100);
        puts(buf);
     
        lseek(fd, 0 , SEEK_SET);
        write(fd, "33 4", 5);
        
        lseek(fd, 0 , SEEK_SET);
        read(fd, buf, 100);
        puts(buf);
    }   
    

    output:

    invincible@ubuntu:~/Desktop/my_mods/mydev$ gcc user_app.c -o user_app
    invincible@ubuntu:~/Desktop/my_mods/mydev$ sudo insmod mydev.ko 
    invincible@ubuntu:~/Desktop/my_mods/mydev$ ./user_app 
    irq = 20
    mode = 1
    
    irq = 33
    mode = 4
    
    invincible@ubuntu:~/Desktop/my_mods/mydev$ 
    
    

    4. 补充

    proc_create是在kernel 3.10以及之后的版本中新增的,用于替换之前的create_proc_entry

    kernel 3.9

    include/linux/proc_fs.h
    
    extern struct proc_dir_entry *create_proc_entry(const char *name, umode_t mode,
                            struct proc_dir_entry *parent);
          
    static inline struct proc_dir_entry *create_proc_entry(const char *name,
        umode_t mode, struct proc_dir_entry *parent) { return NULL; }
    
    
    
    fs/proc/generic.c
    struct proc_dir_entry *create_proc_entry(const char *name, umode_t mode,
                                             struct proc_dir_entry *parent)
    {
            struct proc_dir_entry *ent;
            nlink_t nlink;
    
    
            if (S_ISDIR(mode)) {
                    if ((mode & S_IALLUGO) == 0)
                            mode |= S_IRUGO | S_IXUGO;
                    nlink = 2;
            } else {
                    if ((mode & S_IFMT) == 0)
                            mode |= S_IFREG;
                    if ((mode & S_IALLUGO) == 0)
                            mode |= S_IRUGO;
                    nlink = 1;
            }
    
    
            ent = __proc_create(&parent, name, mode, nlink);
            if (ent) {
                    if (proc_register(parent, ent) < 0) {
                            kfree(ent);
                            ent = NULL;
                    }
            }
            return ent;
    }
    EXPORT_SYMBOL(create_proc_entry);
    
    

    kernel 3.10

    include/linux/proc_fs.h
    
    extern struct proc_dir_entry *proc_create_data(const char *, umode_t,
                               struct proc_dir_entry *,
                               const struct file_operations *,
                               void *);
                
    static inline struct proc_dir_entry *proc_create(
        const char *name, umode_t mode, struct proc_dir_entry *parent,
        const struct file_operations *proc_fops)
    {
        return proc_create_data(name, mode, parent, proc_fops, NULL);
    }
    
    
    fs/proc/generic.c
    struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
                        struct proc_dir_entry *parent,
                        const struct file_operations *proc_fops,
                        void *data)
    {
        struct proc_dir_entry *pde;
        if ((mode & S_IFMT) == 0)
            mode |= S_IFREG;
    
        if (!S_ISREG(mode)) {
            WARN_ON(1); /* use proc_mkdir() */
            return NULL;
        }
    
        if ((mode & S_IALLUGO) == 0)
            mode |= S_IRUGO;
        pde = __proc_create(&parent, name, mode, 1);
        if (!pde)
            goto out;
        pde->proc_fops = proc_fops;
        pde->data = data;
        if (proc_register(parent, pde) < 0)
            goto out_free;
        return pde;
    out_free:
        kfree(pde);
    out:
        return NULL;
    }
    EXPORT_SYMBOL(proc_create_data);
    

    两者的区别主要就是proc_createfile_operation作为参数传递,而proc_create_data是创建了proc_dir_entry之后再设置file_operation

    本文主要内容来自参考资料1

    5. 参考资料

    1. LINUX KERNEL DEVELOPMENT – CREATING A PROC FILE AND INTERFACING WITH USER SPACE
    2. The Linux Kernel Module Programming Guide

    相关文章

      网友评论

        本文标题:如何使用内核API函数 proc_create?

        本文链接:https://www.haomeiwen.com/subject/xjomfhtx.html