- 论坛徽章:
- 0
|
本帖最后由 cmqy 于 2016-04-14 15:12 编辑
/proc是个用于与内核进行交流的ramfs,渊源不作介绍,仅记录自己的认知
在以往的迷糊理解中,认为seq_file是对在/proc创建文件的另一种实现,然而并不是,先说说seq_file的/proc的关系seq_file仅仅是/proc文件向用户层输出的一种实现,为了克服之前不能输出大于1页数据的缺陷理解/proc文件系统之前,最好先看看VFS结构。
创建/proc文件的步骤为:
1、创建/proc条目- struct proc_dir_entry *entry;
- entry=create_proc_entry("proctest",0666,NULL);
复制代码 struct proc_dir_entry是内核保存/proc条目的结构体,看看create_proc_entry的声明- 109 extern struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
- 110 struct proc_dir_entry *parent);
复制代码 @name是创建的proc文件的名字,@mode是访问权限,@parent是父目录的proc_dir_entry结构体
2、关联操作集
看看proc_dir_entry的结构体原型 2.6.32的- struct proc_dir_entry {
- 52 unsigned int low_ino;
- 53 unsigned short namelen;
- 54 const char *name;
- 55 mode_t mode;
- 56 nlink_t nlink;
- 57 uid_t uid;
- 58 gid_t gid;
- 59 loff_t size;
- 60 const struct inode_operations *proc_iops;
- 61 /*
- 62 * NULL ->proc_fops means "PDE is going away RSN" or
- 63 * "PDE is just created". In either case, e.g. ->read_proc won't be
- 64 * called because it's too late or too early, respectively.
- 65 *
- 66 * If you're allocating ->proc_fops dynamically, save a pointer
- 67 * somewhere.
- 68 */
- 69 const struct file_operations *proc_fops;
- 70 struct proc_dir_entry *next, *parent, *subdir;
- 71 void *data;
- 72 read_proc_t *read_proc;
- 73 write_proc_t *write_proc;
- 74 atomic_t count; /* use count */
- 75 int pde_users; /* number of callers into module in progress */
- 76 spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
- 77 struct completion *pde_unload_completion;
- 78 struct list_head pde_openers; /* who did ->open, but not ->release */
- 79 };
- 3.10的
- 32 struct proc_dir_entry {
- 33 unsigned int low_ino;
- 34 umode_t mode;
- 35 nlink_t nlink;
- 36 kuid_t uid;
- 37 kgid_t gid;
- 38 loff_t size;
- 39 const struct inode_operations *proc_iops;
- 40 const struct file_operations *proc_fops;
- 41 struct proc_dir_entry *next, *parent, *subdir;
- 42 void *data;
- 43 atomic_t count; /* use count */
- 44 atomic_t in_use; /* number of callers into module in progress; */
- 45 /* negative -> it's going away RSN */
- 46 struct completion *pde_unload_completion;
- 47 struct list_head pde_openers; /* who did ->open, but not ->release */
- 48 spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
- 49 u8 namelen;
- 50 char name[];
- 51 };
复制代码 挂操作集的方式有使用file_operations和外面的write_proc和read_proc,新版本的内核不提供write_proc/read_proc了。
seq_file的操作方式- struct seq_operations normal_seq_ops =
- {
- .start=nor_start,
- .show=nor_show,
- .next=nor_next,
- .stop=nor_stop
- };
- int normal_open(struct inode *inode, struct file *file)
- {
- return seq_open(file,&normal_seq_ops);
- }
- static struct file_operations fops =
- {
- .owner=THIS_MODULE,
- .llseek=seq_lseek,
- .read=seq_read,
- .write=seq_write,
- .open=normal_open,
- .release=seq_release
- };
复制代码 使用file_operations对文件进行操作,用于只读的话,只需要挂上open和write函数就行,其他函数可以使用内核提供,其中open简单的返回seq_open,秘密在于其参数struct *seq_operations,需要自己实现4个回调函数。用于迭代产生内容。其调用规则为:启动时调用start,start执行后,如果返回非NULL,则返回值作为show的参数v调用show用于向/proc文件中输出数据,然后调用next,参数v仍为start返回结果,如果next返回非空则调用show,如此往复循环,返回空则调用stop。之后再调用start,直到start返回为NULL,最后仍会调用一次stop。seq_operations的原型为- 35 struct seq_operations {
- 36 void * (*start) (struct seq_file *m, loff_t *pos);
- 37 void (*stop) (struct seq_file *m, void *v);
- 38 void * (*next) (struct seq_file *m, void *v, loff_t *pos);
- 39 int (*show) (struct seq_file *m, void *v);
- 40 };
复制代码 start返回一个存储了数据的空间,是自己创建的,并在调用show时作为v参数传给show,show负责向用户展示里面的内容。start的*pos是递增的,可以用于判断什么时候返回NULL结束迭代。
show可以对输出内容作格式化处理,参数v含有内容,参数m为输出接口,可使用以下函数- 82 int seq_putc(struct seq_file *m, char c);
- 83 int seq_puts(struct seq_file *m, const char *s);
- 86 int seq_printf(struct seq_file *, const char *, ...)
复制代码 对于路径及链表,还有其它函数可以使用,详情见内核文档Document/filesystem/seq_file.txt。其中还有一段话,说明其中指向seq_file的指针在调用seq_open的时候被存在struct file的private域中- On a successful open, seq_open() stores the struct seq_file pointer in
- file->private_data. If you have an application where the same iterator can
- be used for more than one file, you can store an arbitrary pointer in the
- private field of the seq_file structure; that value can then be retrieved
- by the iterator functions
复制代码 对于2.6.32之类的老内核,可以使用proc_dir_entry里的write_proc和read_proc函数对proc文件进行操作- 46 typedef int (read_proc_t)(char *page, char **start, off_t off,
- 47 int count, int *eof, void *data);
- 48 typedef int (write_proc_t)(struct file *file, const char __user *buffer,
- 49 unsigned long count, void *data);
复制代码 对于read_proc
page 是内核创建的页,start是如果使用自己创建的内存空间,则将指针的地址赋给start,off是希望从proc中读开始取的偏移,count是page页提供的大小,eof 当传递结束时,应该把*eof赋1,data是使用proc_create_data创建proc_dir_entry时传的指针
对于write_proc
file是file结构体,buffer是用户空间,count是用户数据长度,data和read_proc一样。
3、创建目录和在指定目录下创建条目
使用proc_mkdir()可以在/proc下创建目录
而想要在/proc/net下创建文件,在内核2.6.32中的操作方式是讲create_proc_entry的parent参数设为init_net.proc_net,不同版本内核不一样
最后附上示例代码:
- /*
- *创建/proc/net/testdir/目录下ro_read_proc,rw_normal,rw_write_proc三个文件
- *rw_normal用seq_file实现,只读
- *rw_write_proc用write_proc和read_proc实现,读写,用于输入需要在内核打开的文件名
- *ro_read_proc用create_proc_read_entry实现的只读,用打开并输出从rw_write_proc输入的文件名,文件应小于3072字节
- */
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/proc_fs.h>
- #include <linux/fs.h>
- #include <linux/seq_file.h>
- #include <linux/slab.h>
- #include <linux/string.h>
- #include <asm/uaccess.h>
- #include <net/net_namespace.h> //init_net.proc_net
- #include <linux/err.h> //IS_ERR
- #include <linux/dcache.h> //dentry
- #include <asm/segment.h> //use for operating files in kernel space
- #include <linux/buffer_head.h>
- MODULE_AUTHOR("Liaosp");
- MODULE_LICENSE("Dual BSD/GPL");
- #define MAXPATHSIZE 256
- static char pathbuff[MAXPATHSIZE];
- void * nor_start(struct seq_file *m, loff_t *pos)
- {
- if(*pos>0)
- return NULL;
- return pathbuff;
- }
- void nor_stop(struct seq_file *m, void *v)
- {
- }
- void * nor_next(struct seq_file *m, void *v, loff_t *pos)
- {
- return NULL;
- }
- int nor_show(struct seq_file *m, void *v)
- {
- seq_printf(m,"%s",(char *)v);
- return 0;
- }
- struct seq_operations normal_seq_ops =
- {
- .start=nor_start,
- .show=nor_show,
- .next=nor_next,
- .stop=nor_stop
- };
- int normal_open(struct inode *inode, struct file *file)
- {
- return seq_open(file,&normal_seq_ops);
- }
- static struct file_operations fops =
- {
- .owner=THIS_MODULE,
- .llseek=seq_lseek,
- .read=seq_read,
- .write=seq_write, //这里是个严重的BUG,write函数得自己写
- .open=normal_open,
- .release=seq_release
- };
- //operating files in kernel
- struct file *file_open(const char *path, int flag,int right)
- {
- mm_segment_t oldfs;
- struct file *filp=NULL;
- int err=0;
- oldfs=get_fs();
- set_fs(get_ds());
- filp=filp_open(path,flag,right);
- if(IS_ERR(filp))
- {
- printk("open %s error\n",path);
- err=PTR_ERR(filp);
- return NULL;
- }
- set_fs(oldfs);
- return filp;
- }
- void file_close(struct file *file)
- {
- filp_close(file,NULL);
- }
- int file_read(struct file *file,unsigned char *data, unsigned int size,unsigned long long offset)
- {
- mm_segment_t oldfs;
- int ret;
- oldfs=get_fs();
- set_fs(get_ds());
- ret=vfs_read(file, data, size, &offset);
- set_fs(oldfs);
- return ret;
- }
- // what is @start @off @eof @data mean?
- static int read_ro(char *page,char **start, off_t off, int count, int *eof, void *date)
- {
- struct file *fp;
- unsigned long re=0;
- printk("%s\n",pathbuff);
- fp=file_open(pathbuff,O_RDONLY,0);
- printk("file open success :%s\n",fp->f_path.dentry->d_name.name);
- re=file_read(fp,page,count,off);
- file_close(fp);
- if(re<=0)
- // re=snprintf(page,10,"in read_ro in ro\n");
- *eof=1;
- return re;
- }
- //@page 是内核创建的页
- //@start 如果使用自己创建的内存空间,则将指针的地址赋给start
- //@off 希望从proc中读开始取的偏移
- //@count page页提供的大小
- //@eof 当传递结束时,应该把*eof赋1
- //@data proc_create_data传的指针
- static int rw_r_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
- {
- int re=0;
- printk("in proc_read\noff:%d conut:%d\n",off,count);
- re=snprintf(page,MAXPATHSIZE,"%s",pathbuff);
- *eof=1;
- return re;
- }
- static int rw_w_proc(struct file *file, const char __user *buffer, unsigned long count, void *data)
- {
- int re=0;
- re=copy_from_user(pathbuff,buffer,count);
- pathbuff[count-1]='\0';
- printk("%s",pathbuff);
- return count;
- }
- struct proc_dir_entry *dir; // /proc/net/<dir>
- static int exproc_init(void)
- {
- memset(pathbuff,0,MAXPATHSIZE);
- strcpy(pathbuff,"echo the path into this file more len \n");
- struct proc_dir_entry *rw_normal; //using file_operations
- struct proc_dir_entry *rw_write_proc; //rw using write_proc
- struct proc_dir_entry *ro_read_proc; //ro using create_proc_read_entry
- // dir=create_proc_entry("/proc/net/testdir",0666,NULL);
- // don't work
- // struct file *filp = filp_open("/proc/net",O_RDONLY,0);
- // parent=PDE(filp->f_dentry->d_inode);
- // dir=create_proc_entry("testdir",0666,parent);
- // can't work in kernel 2.6.35
- dir=proc_mkdir("testdir",init_net.proc_net);
- rw_normal=create_proc_entry("rw_normal",0444,dir);
- rw_write_proc=create_proc_entry("rw_write_proc",0666,dir);
- ro_read_proc=create_proc_read_entry("ro_read_proc",0,dir,read_ro,NULL);
- rw_write_proc->read_proc=rw_r_proc;
- rw_write_proc->write_proc=rw_w_proc;
- rw_normal->proc_fops=&fops;
- }
- static void exproc_exit(void)
- {
- remove_proc_entry("rw_normal",dir);
- remove_proc_entry("rw_write_proc",dir);
- remove_proc_entry("ro_read_proc",dir);
- remove_proc_entry("testdir",init_net.proc_net);
- }
- module_init(exproc_init);
- module_exit(exproc_exit);
复制代码 这个文本编辑器真恶心,害我写了两次,还差点要写三次 |
评分
-
查看全部评分
|