Linux内核模块编程-proc文件系统进阶

Posted zhangyifei216

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux内核模块编程-proc文件系统进阶相关的知识,希望对你有一定的参考价值。

proc作为输入

关于proc的一些基本概念和基本使用以及一些核心数据结构可以参考我的上一篇文章
在上一篇文章中提到了利用proc作为内核空间传递数据给用户空间的一种方式,那么proc能否作为用户空间传递数据给内核空间的一种手法呢?答案当然是可以的,熟悉linux的读者应该都知道我们不光光可以读取proc文件中的内容还是可以向proc文件中写入要传递的数据的,那么如何通过proc来向内核输入信息呢,通过查看proc_dir_entry发现,在这个proc核心数据结构中不仅仅有一个read_proc的函数指针,而且还有一个write_proc函数指针。恭喜你猜对了,通过这个钩子函数的确就可以实现利用proc向内核空间传递数据,但是随着内核的发展,内核的开发者采用了另外一种方式来提供proc的读写钩子函数。在2,6内核中两种方式都是可以的,后来笔者发现在我使用的3.10内核上居然已经没有第一种了。

2.6系列的内核提供了两种方法来操作proc
一种就是:

    read_proc_t *read_proc;
    write_proc_t *write_proc;

第二种就是:

    struct inode_operations * proc_iops;
    const struct file_operations * proc_fops;
    可以参考linux内核文件系统相关的知识

可是到了3.10系列内核后就只有第二种了,所以为了兼容不同内核版本,在操作proc的时候最好按照标准的VFS的接口形式来操作VFS两种不同版本内核对proc_dir_entry的定义
下面是在2.6内核以及3.10内核中关于proc_dir_entry这个核心数据结构的定义的区别。

struct proc_dir_entry 
    unsigned int low_ino;
    unsigned short namelen;
    const char *name;
    mode_t mode;
    nlink_t nlink;
    uid_t uid;
    gid_t gid;
    loff_t size;
    struct inode_operations * proc_iops;
    const struct file_operations * proc_fops;
    get_info_t *get_info;
    struct module *owner;
    struct proc_dir_entry *next, *parent, *subdir;
    void *data;
    read_proc_t *read_proc;
    write_proc_t *write_proc;
    atomic_t count;     /* use count */
    int deleted;        /* delete flag */
    void *set;
;
2.6.18的内核
struct proc_dir_entry 
    unsigned int low_ino;
    umode_t mode;
    nlink_t nlink;
    kuid_t uid;
    kgid_t gid;
    loff_t size;
    const struct inode_operations *proc_iops;
    const struct file_operations *proc_fops;
    struct proc_dir_entry *next, *parent, *subdir;
    void *data;
    atomic_t count;     /* use count */
    atomic_t in_use;    /* number of callers into module in progress; */
            /* negative -> it's going away RSN */
    struct completion *pde_unload_completion;
    struct list_head pde_openers;   /* who did ->open, but not ->release */
    spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
    u8 namelen;
    char name[];
;
3.10的内核

可以看出在3.10内核中已经废除了第一种方式来操作proc文件了。下面以第二种方式来操作proc作为一个示列,通过这个示列来告诉读者如何去使用第二种方式来操作文件。

下面通过标准的VFS接口的形式来操作proc。

模块的初始化

#define MESSAGE_LENGTH 80
#define PROC_ENTRY_FILENAME "rw_test"
static char Message[MESSAGE_LENGTH];
struct proc_dir_entry *Our_Proc_File;

int init_module(void)

    int rv = 0;
    Our_Proc_File = create_proc_entry(PROC_ENTRY_FILENAME,0644,NULL);
//省去了这一行,改用VFS统一接口的形式操作proc
//  Our_Proc_File->read_proc = procfile_read;  
    Our_Proc_File->mode = S_IFREG | S_IRUGO;
    Our_Proc_File->uid = 0;
    Our_Proc_File->gid = 0;
    Our_Proc_File->size = 80; 
//标准的VFS接口形式
    Our_Proc_File->proc_iops = &Inode_Ops_4_Our_Proc_File;
    Our_Proc_File->proc_fops = &File_Ops_4_Our_Proc_File;
    printk(KERN_INFO "Trying to craete /proc/rw_test:\\n");
    if(Our_Proc_File == NULL) 
        rv = -ENOMEM;
        remove_proc_entry(PROC_ENTRY_FILENAME,proc_root);
        printk(KERN_INFO "Error: Could not initialize /proc/rw_test\\n");
     else 
        printk(KERN_INFO "Success!\\n");
     else 
        printk(KERN_INFO "Success!\\n");
    
    return rv;

模块的注销

void cleanup_module(void)

    remove_proc_entry(PROC_ENTRY_FILENAME,NULL);
    printk(KERN_INFO "/proc/test1 removed\\n");

VFS接口的注册

static struct file_operations File_Ops_4_Our_Proc_File = 
    .read = module_output,
    .write = module_input,
    .open = module_open,
    .release = module_close,
;
//在inode层面做了一个权限检测
static struct inode_operations Inode_Ops_4_Our_Proc_File = 
    .permission = module_permission,
;

open/close的实现

只是简单的增加和减小模块的引用计数
int module_open(struct inode *inode,struct file *file)

    try_module_get(THIS_MODULE);
    return 0;


int module_close(struct inode *inode,struct file *file)

    module_put(THIS_MODULE);
    return 0;

read/write实现

static ssize_t module_input(struct file *filp,const char* buff,size_t len,loff_t *off)

//写数据到内核空间,通过get_user将字符一个一个存入到内核空间的Message的字符数组中
    int i;
    for(i = 0;i < MESSAGE_LENGTH-1 && i < len;i++)
        get_user(Message[i],buff + i);

    Message[i] = '\\0';
    return i;


//读取内核空间的数据,将内核空间的Message字符数组的值通过put_user输出到用户空间的buffer中
static ssize_t module_output(struct file *filp,
            char *buffer,
            size_t length,
            loff_t * offset)


    static int finished = 0;
    int i;
    char message[MESSAGE_LENGTH + 30];
    if(finished) 
        finished = 0;
        return 0;
    
    sprintf(message,"Last input: %s",Message);
    for(i = 0;i < length && message[i];i++)
        put_user(message[i],buffer+i);

    finished = 1;
        return i;

权限检测

static int module_permission(struct inode *inode,int op) 

//如果当前用户UID为0,则运行操作proc文件,否则提示权限问题
    if(current->real_cred->uid == 0)
        return 0;

    return -EACCES;

载入模块进行测试

insmod procfs.ko
[root@localhost kernel_module]# echo "test" >> /proc/rw_test 
[root@localhost kernel_module]# cat /proc/rw_test 
Last input: test
普通用户读取:
[test@localhost ~]$ cat /proc/rw_test 
cat: /proc/rw_test: Permission denied
与50位技术专家面对面 20年技术见证,附赠技术全景图

以上是关于Linux内核模块编程-proc文件系统进阶的主要内容,如果未能解决你的问题,请参考以下文章

高手进阶:Linux操作系统驱动编译与运行

Linux内核安全模块学习-内核密钥管理子系统

重构linux系统

linux内核编译及系统裁减

linux 使用 /proc 文件系统

Linux下/proc目录简介