很多内核模块都在/proc目录添加条目,用文件读写的方式来实现用户态和内核交换数据。
在新的内核,创建proc条目的接口通常使用:
1 2 3 4 5 6 7  | extern struct proc_dir_entry *proc_create_data(const char *, umode_t,                           struct proc_dir_entry *,                           const struct file_operations *,                           void *);struct proc_dir_entry *proc_create(const char *name, umode_t mode,     struct proc_dir_entry *parent, const struct file_operations *proc_fops); | 
proc_create()的第一个参数是文件名;mode是读写权限;parent是父目录,若为NULL,则创建在/proc下面。proc_fops是struct file_operations类型,指定文件操作回调函数。
proc目录可以用proc_mkdir()接口创建。
proc_create_data()比proc_create()多一个参数,传入一个指针,该指针存储在proc_inode.data。用户可以文件操作回调函数中读取该指针,这样就可以多个proc文件共享回调函数,根据data区分不同的proc文件。
删除proc条目,使用下述接口:
1  | extern void proc_remove(struct proc_dir_entry *); | 
读接口如下:
1 2  | static ssize_t proc_read(struct file *file,    char __user *data, size_t len, loff_t *off) | 
第一个参数表示打开的文件,data是用户存储数据的指针,len是要读取的长度,off是当前offset。
通过file->f_inode,可以获得inode指针,通过PDE_DATA(),可以获得创建时传入的data。
read接口需要自己负责off的移动。返回值0表示EOF,返回正数表示成功读取的长度,返回负数表示错误。
写接口如下:
1 2  | static ssize_t proc_write(struct file *file,    const char __user *data, size_t len, loff_t *off) | 
和读接口类似,返回正数表示成功写入的长度,返回负数表示错误,返回0,表示本次写入0,用户态程序可尝试下次再写,因此如果是buffer满,不要返回0,否则用户态会不停的写。
同样off需要本接口自己负责移动。
内核模块代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119  | /* * A demo module demonstrating how to add a proc entry to /proc * * Copyright (C) 2018 Yuan Jianpeng <yuanjp@hust.edu.cn> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. */#include <linux/module.h>#include <linux/proc_fs.h>#include <linux/uaccess.h>#define PROC_ENTRY_NUM          2#define PROC_ENTRY_NAME         "test"#define PROC_DATA_SIZE          16static char test_data[PROC_ENTRY_NUM][PROC_DATA_SIZE];static ssize_t proc_read(struct file *file,    char __user *data, size_t len, loff_t *off){    int remain, readed;    long no;         no = (long)PDE_DATA(file->f_inode);    if (no < 0 || no >= PROC_ENTRY_NUM)         return -EINVAL;         /* return 0 indicates EOF */    if (*off >= PROC_DATA_SIZE)        return 0;    remain = PROC_DATA_SIZE - *off;    readed = (len > remain) ? remain : len;    if (copy_to_user(data, &test_data[no][*off], readed))        return -EFAULT;    /* move off for next read */    *off += readed;    /* return data length actually readed */    return readed;}static ssize_t proc_write(struct file *file,    const char __user *data, size_t len, loff_t *off){    int remain, written;    long no;         no = (long)PDE_DATA(file->f_inode);    if (no < 0 || no >= PROC_ENTRY_NUM)         return -EIO;    /* return 0 is wrong,         the userspace command (echo for example) will attemp write again and again */    if (*off >= PROC_DATA_SIZE)        return -EFBIG;    remain = PROC_DATA_SIZE - *off;    written = (len > remain) ? remain : len;    if (copy_from_user(&test_data[no][*off], data, written))        return -EIO;    /* move off for next writing more data */    *off += written;    return written;}static struct proc_dir_entry *proc_entry[PROC_ENTRY_NUM];static const struct file_operations proc_file_fops = {    .owner = THIS_MODULE,    .read = proc_read,    .write = proc_write,};static int __init mod_init(void){    long i;    char name[32];    for (i = 0; i < PROC_ENTRY_NUM; i++) {        snprintf(name, sizeof(name), PROC_ENTRY_NAME "%ld", i);        proc_entry[i] = proc_create_data(name, 0644, NULL, &proc_file_fops, (void *)i);        if (!proc_entry[i])            goto err;    }    return 0;err:    for (i = 0; i < PROC_ENTRY_NUM; i++) {        if (proc_entry[i])            proc_remove(proc_entry[i]);    }    return -EBUSY;}static void __exit mod_exit(void){    long i;    for (i = 0; i < PROC_ENTRY_NUM; i++) {        proc_remove(proc_entry[i]);    }}module_init(mod_init);module_exit(mod_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("Yuan Jianpeng"); |