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的第一个参数是要创建的目录的名字,第二个参数是指向父目录结构的指针。需要注意一下几点:
创建过程没有对于名字的检查,完全可以调用proc_mkdir 创建出一堆同样名字的文件(检查一下会死啊!)
其实名字可以包含路径,例如“dev/new_proc”, 如果存在dev目录,函数会在dev目录下创建new_proc目录。
如果第二个参数是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
第一个参数:为啥叫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。回到参数的说明:
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的值。
count 参数与read中的count一致
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
如果设置了这个参数就不会有第二次的下发了。
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
- proc文件系统读取
- proc文件系统
- proc文件系统
- /proc 文件系统
- proc 文件系统
- proc文件系统
- /proc文件系统
- /proc 文件系统
- proc文件系统
- proc文件系统
- proc文件系统
- proc文件系统
- /proc 文件系统
- /proc文件系统
- Proc文件系统
- /proc文件系统
- PROC 文件系统
- Proc文件系统
- TCP--client
- TensorFlow实战——RNN(LSTM)——预测sin函数
- git代码提交的一些常规流程
- 使用系统的某些block api(如UIView的block版本写动画时),是否也考虑引用循环问题?
- vue 脚手架的搭建和目录结构
- proc文件系统读取
- Linux常用基本命令
- lock和synchornized教程
- TCP的三次握手与四次挥手
- python正则表达式
- 微信-Android 浏览器刷新有缓存?
- Mac OS安装启动盘的制作
- 我的markdown 练习
- Tomcat源码解析(一)下载源码与导入eclipse