proc文件系统读取

来源:互联网 发布:追着打印软件2015 编辑:程序博客网 时间:2024/06/05 22:30

Proc文件系统读取

在以前的版本:(copy 学长的)

在一般的情况下,需要以下函数:

struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent)struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent);void remove_proc_entry(const char *name, struct proc_dir_entry *parent);

函数 proc_mkdir用于在proc文件系统下创建文件夹, proc_mkdir的第一个参数是要创建的目录的名字,第二个参数是指向父目录结构的指针。需要注意一下几点:

  1. 创建过程没有对于名字的检查,完全可以调用proc_mkdir 创建出一堆同样名字的文件(检查一下会死啊!)

  2. 其实名字可以包含路径,例如“dev/new_proc”, 如果存在dev目录,函数会在dev目录下创建new_proc目录。

  3. 如果第二个参数是NULL,会在根目录也就是/proc/目录下创建目录

函数的返回值就是 新创建目录对应的proc_dir_entry, 保存这个就可以用来在此目录下创建文件啦,其实,即使不保存,利用上面介绍的第二点性质也可以在目录下创建文件,而且删除目录只用知道路径就可以了~

函数create_proc_entry用来创建文件,mode 参数如果为NULL的话默认的文件访问权限是 755,其他的参数与proc_mkdir 类似

函数remove_proc_entry用来删除创建的目录或者文件,有意思的是,这个函数只需要知道名字和父目录就可以删除了。

需要了解下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;};

其他的参数可以忽略,这里需要注意的是两个成员变量:

read_proc 和 write_proc

这两个变量的类型如下:

typedef int (read_proc_t)(char *page, char **start, off_t off, int count, int *eof, void *data);
typedef int (write_proc_t)(struct file *file, const char __user *buffer, unsigned long count, void *data);

对proc文件的读写操作,最终将转化为对read_proc和write_proc的调用。

看到这里明白了吧,只要在内核里注册proc文件,实现read_proc 和write_proc函数,然后设置proc_dir_entry对应成员变量的值,在用户态进行读写就可以和内核交互了

解释这两个函数的参数:

对于read_proc_t

  1. 第一个参数:为啥叫page?答案就是如果对proc文件调用读操作,内核会分配一个页大小的缓冲区。如何输出大于一个页的数据呢,这得依赖于第二个和第三个参数了。

    为了理解第二三个参数,回忆下与文件操作相关的系统调用:

int open(const char *pathname, int flags);off_t lseek(int fildes, off_t offset, int whence);ssize_t read(int fd, void *buf, size_t count);ssize_t write(int fd, const void *buf, size_t count);

​ 对于proc文件,一次read操作最多只能读取一个page的数据,如果需要读取大于一个页的数据需要保存read的返回 值,然后使用lseek设定offset,然后再次调用read。回到参数的说明:

  1. start和off参数:off对应于lseek里面的offset(lseek whence为SEEK_END,offset为负 的情况下,传进来的off为零,具体原因待考古)。

    如果不设置*start的值,off的取值只能在[0, count - 1]之间,且能够读取的数据大小为:count - off。可以理解系统拷贝了 [page + off, page+count - 1]之间的数据到用户的buffer里。如果off的取值超出范围,read将读不到数据。

    如果设置了*start的值,系统认为*start指向的地址就是off指定的地址,off的值会被忽略,系统会拷贝[start, start+count-1]之间的数据到用户空间。当然我们在实现start地址的定位时,可能会需要off的值。

  2. count 参数与read中的count一致

  3. eof参数,设置了这个参数表明不想再提供数据了,神马意思呢?

    如果不设置这个参数,对于上面说的start为空的情况,read_proc返回后,系统如果发现 (count - off) < count 会接着下发写请求,读取off大小的数据。

    例如:有这样的读取操作:

    lseek(fd, 2, SEEK_SET); read(fd, buff_r, 30)
    第一次调用read_proc, off=2, count=30, 由于我们没有设置start的值,将读取28字节的数据,由于没有设置*eof, 系统会再次下发read_proc

    第二次调用read_proc, off=30, count=2, 在参数start的说明里提到,对于这个调用,系统默认读不到数据(怨念啊,为啥要下发)

    于是read返回28

    如果设置了这个参数就不会有第二次的下发了。

  4. data参数,这个是给驱动程序预留的参数。

    对于write_proc函数,参数是很简单的,需要说明的只有一点,就是write_proc的第二个参数buffer是用户态的地址,需要用copy_from_user从用户态把数据拷到内核态的缓冲区里。

完整的代码:

#include <linux/module.h>#include <linux/kernel.h>#include <linux/proc_fs.h>#include <linux/sched.h>#include <linux/uaccess.h>#define STRINGLEN 1024char global_buffer[STRINGLEN];//分配一块大小为1024字节的内存struct proc_dir_entry *example_dir,*hello_file,                      *current_file,*symlink;int proc_read_current(char *page,char **start,off_t off,int count,        int *eof,void *data){//用户读取current文件时,内核调用这个函数    int len;    try_module_get(THIS_MODULE);//模块引用计数增加1,保证模块不会被卸载    len=sprintf(page,"cunt process usages: \nname: %s \npid : %d\n",current->comm,current->pid);    //告诉用户,读出的文件内容是一行字符串,说明了当前进程的名字和pid    module_put(THIS_MODULE);//模块引用计数减1    return len;}int proc_read_hello(char *page,char **start,off_t off,int count,        int *eof,void *data){//用户读取hello文件时,内核调用这个函数    int len;    try_module_get(THIS_MODULE);    len=sprintf(page,"hello message:%s\nwrite:%s\n",current->comm,            global_buffer);    //给global_buffer(前面申请的1024大小的内存)的前面加上两行文字,作为          //用户读出的内容    module_put(THIS_MODULE);    return len;}int proc_write_hello(struct file *file,const char *buffer,unsigned long count, void *data){    //用户写hello文件时,内核调用这个函数    int len;    try_module_get(THIS_MODULE);    if(count>=STRINGLEN)//如果写的内容超过1024,则截断        len=STRINGLEN-1;    else        len=count;    copy_from_user(global_buffer,buffer,len);    //从用户的缓冲区获得要写的数据      global_buffer[len]='\0';//给字符串加个0,表示结束    module_put(THIS_MODULE);    return len;}static int init(void){//将会在加载模块的时候被调用,在加载模块的时候创建proc文件    example_dir=proc_mkdir("proc_test",NULL);//创建目录/proc/proc_test    current_file=create_proc_read_entry("current",0666,example_dir,            proc_read_current,NULL);//创建目录/proc/proc_test/current    hello_file=create_proc_entry("hello",0644,example_dir);    strcpy(global_buffer,"hello");    hello_file->read_proc=proc_read_hello;    hello_file->write_proc=proc_write_hello;    //创建一个可读写的文件/proc/proc_test/hello    symlink=proc_symlink("current_too",example_dir,"current");    //创建一个链接/proc/proc_test/current_too,指向current    return 0;}static void cleanup(void ){//卸载模块,把创建的目录和文件都删除    remove_proc_entry("current_too",example_dir);    remove_proc_entry("hello",example_dir);    remove_proc_entry("current",example_dir);    remove_proc_entry("proc_test",NULL);}module_init(init);module_exit(cleanup);

内核4.4(ubuntu16.04)下

在内核4.4下,proc文件系统读写发生了变化,创建文件的函数发生变化,使用

proc_dir_enty proc_create(char* name,mode_t mode,proc_dir_enty *father,struct file_operations* operations)

name:创建的文件名字

mode_t:一般都是0666,(第一个0是特殊权限)

father:父文件夹,如果没有,在根目录下,为NULL

operations:文件读写操作函数指针的结构体,类似于驱动程序的文件读取(讲道理一样的结构体)

文件读写可能有变化,但是目前没有明确证据,只是推测。

我的读写示例:

#include<linux/kernel.h>#include<linux/fs.h>#include<linux/slab.h>#include<linux/init.h>#include<linux/module.h>#include<asm/uaccess.h>#include<linux/proc_fs.h>#define DEV_MAJOR 83#define DEV_NUM 2#define DEV_INFO  "14130110083_znr dev2 "#define DEV_NAME "znr_dev2"int N;module_param(N, int,S_IRUSR);//此程序可以写入,但不能读struct record{    char* buffer;    int count;    int currentLen;    int next_read_start;};struct proc_dir_entry *mydir,*myfile;struct record *point = NULL;void show_info(void){    printk("****************\n");    printk("\t%s\n",DEV_INFO);    printk("****************\n\n");}ssize_t dev_read(struct file *file, char *buffer,size_t count, loff_t *f_ops){    printk("dev read\n");    if( copy_to_user(buffer,point->buffer,count) )        return -1;    return count;}ssize_t dev_write(struct file *file, char *buffer,size_t count, loff_t *f_ops){    printk("dev write\n");    if( copy_from_user(point->buffer, buffer, count) )        return -1;    return count;}int dev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data){    printk("dev ioctl\n");    return 0;}int dev_open(struct inode *inode, struct file * file){    printk("dev open\n");    return 0;}int dev_release(struct inode *inode, struct file *file){    printk("dev release\n");    return 0;}struct file_operations dev_operations={    .open = dev_open,    .read = dev_read,    .write = dev_write,    .compat_ioctl = dev_ioctl,    .release = dev_release};int proc_write(struct file *file, const char *buffer, unsigned long count, void *data){  //然而写文件的函数没问题    //user write profile will invoke here    printk("proc write\n");   // try_module_get(THIS_MODULE);    int len;    if( count > N)        len = N-1;    else        len = count;    if(copy_from_user(&point->buffer[point->currentLen], buffer, len))      {          printk("write fail\n");      //    module_put(THIS_MODULE);          return -EFAULT;      }    point->currentLen += len;    point->buffer[point->currentLen-1] = '\0';    printk("write success\n");    //module_put(THIS_MODULE);    return len;}void setMem(char* buffer, int len){    int i;    for(i=0;i<N;i++)    {        buffer[i] = '0';    }}int proc_read(char *page, char **start, off_t off, int count, int *eof, void *data){  //这里注释这么多是因为这个读有问题,具体见注释    //user read proc file , kernal will invoke here    printk("proc read\n");  //理论上这个判断是需要的,但是我的程序运行时,off>0 恒1   /* if(off > 0)        {            printk("off > 0 \n");           // *eof = 1;            printk("set *eof = 1\n");        sprintf(page,"oh,fuch, off>0\n");            return 0;        }        */  //在内核版本3.10前,使用“INC_MODULE”宏(不是这么拼写,但是相似)    //try_module_get(THIS_MODULE);   /* if(point->next_read_start > N)        point->next_read_start = 0;*/    int len;  //这条语句运行时,测试程序无法获取到字符串,如果对proc文件cat,会一直不停读,具体问题原因是这个系统调用认为程序没有读完,才会一直读,需要置*eof=1或者*start=NULL,但是当我设置时候会提示eof/start空指针,具体见下文的链接    len = sprintf(page, "hello znr\n");     printk("has read %d\n",len);  /*  len = sprintf(page, "%s\n",&point->buffer[point->next_read_start]);   //同上,而且在程序返回时,或者返回后,会出现一大串问题,这个没有研究透,可能和这个函数的调用有关,或者更换proc_read函数   printk("sprintf ok\n");    for(len = 0;len < point->currentLen; len++)    {        if(point->buffer[point->next_read_start + len] == '\0')            break;        else        printk("%c",point->buffer[point->next_read_start + len]);    }    printk("\n");*/  //使用copy_to_user也是没有输出,但是不会出问题  // copy_to_user(page,point->buffer, len);  // printk("read len: %d\n",++len);   // point->next_read_start += len;    //module_put(THIS_MODULE);   return len;}//proc文件读写函数的映射结构体static struct file_operations proc_operations={    .read = proc_read,    .write = proc_write,};static int dev_init(void){    int ret = -1;    printk("init dev_init\n");    ret = register_chrdev(DEV_MAJOR, DEV_NAME , &dev_operations);    show_info();    mydir = proc_mkdir(DEV_NAME,NULL);    if(!mydir)    {        printk("false to mkdir znr_dev\n");        return 0;    }    myfile = proc_create("znr", 0666, mydir,&proc_operations);    if(!myfile)    {        printk("false to create znr \n");        return 0;    }    if(ret < 0)    {        printk("false to register dev\n");        return 0;    }    else    {        printk("success to register dev\n");        point = (struct record*) kmalloc( sizeof(struct record), GFP_KERNEL );        point->buffer = (char*) kmalloc( sizeof(char) * N, GFP_KERNEL);        setMem(point->buffer,N);        point->count = 0;        point->currentLen = 0;        point->next_read_start = 0;        printk("has malloc sizeof(char)* %d memory !\n",N);        return ret;    }}static void dev_exit(void){    printk("exit dev_exit\n");    remove_proc_entry("znr",mydir);    printk("remove_proc_entry znr \n");    remove_proc_entry(DEV_NAME,NULL);    printk("remove_proc_entry znr_dev2\n");    kfree(point->buffer);    kfree(point);    point = NULL;    printk("release point\n");    unregister_chrdev(DEV_MAJOR,DEV_NAME);}MODULE_DESCRIPTION("znr experiment1 drive");MODULE_LICENSE("GPL");module_init(dev_init);module_exit(dev_exit);

proc文件读函数的问题链接:http://www.linuxdiyf.com/viewarticle.php?id=188715