免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1801 | 回复: 2
打印 上一主题 下一主题

[文件系统] proc探究记录 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2016-04-07 15:18 |只看该作者 |倒序浏览
本帖最后由 cmqy 于 2016-04-14 15:12 编辑

/proc是个用于与内核进行交流的ramfs,渊源不作介绍,仅记录自己的认知

在以往的迷糊理解中,认为seq_file是对在/proc创建文件的另一种实现,然而并不是,先说说seq_file的/proc的关系seq_file仅仅是/proc文件向用户层输出的一种实现,为了克服之前不能输出大于1页数据的缺陷理解/proc文件系统之前,最好先看看VFS结构。
创建/proc文件的步骤为:
1、创建/proc条目
  1. struct proc_dir_entry *entry;
  2. entry=create_proc_entry("proctest",0666,NULL);
复制代码
struct proc_dir_entry是内核保存/proc条目的结构体,看看create_proc_entry的声明
  1. 109 extern struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
  2. 110                                                 struct proc_dir_entry *parent);
复制代码
@name是创建的proc文件的名字,@mode是访问权限,@parent是父目录的proc_dir_entry结构体

2、关联操作集
看看proc_dir_entry的结构体原型 2.6.32的
  1. struct proc_dir_entry {
  2. 52         unsigned int low_ino;
  3. 53         unsigned short namelen;
  4. 54         const char *name;
  5. 55         mode_t mode;
  6. 56         nlink_t nlink;
  7. 57         uid_t uid;
  8. 58         gid_t gid;
  9. 59         loff_t size;
  10. 60         const struct inode_operations *proc_iops;
  11. 61         /*
  12. 62          * NULL ->proc_fops means "PDE is going away RSN" or
  13. 63          * "PDE is just created". In either case, e.g. ->read_proc won't be
  14. 64          * called because it's too late or too early, respectively.
  15. 65          *
  16. 66          * If you're allocating ->proc_fops dynamically, save a pointer
  17. 67          * somewhere.
  18. 68          */
  19. 69         const struct file_operations *proc_fops;
  20. 70         struct proc_dir_entry *next, *parent, *subdir;
  21. 71         void *data;
  22. 72         read_proc_t *read_proc;
  23. 73         write_proc_t *write_proc;
  24. 74         atomic_t count;         /* use count */
  25. 75         int pde_users;  /* number of callers into module in progress */
  26. 76         spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
  27. 77         struct completion *pde_unload_completion;
  28. 78         struct list_head pde_openers;   /* who did ->open, but not ->release */
  29. 79 };
  30. 3.10的
  31. 32 struct proc_dir_entry {
  32. 33         unsigned int low_ino;
  33. 34         umode_t mode;
  34. 35         nlink_t nlink;
  35. 36         kuid_t uid;
  36. 37         kgid_t gid;
  37. 38         loff_t size;
  38. 39         const struct inode_operations *proc_iops;
  39. 40         const struct file_operations *proc_fops;
  40. 41         struct proc_dir_entry *next, *parent, *subdir;
  41. 42         void *data;
  42. 43         atomic_t count;         /* use count */
  43. 44         atomic_t in_use;        /* number of callers into module in progress; */
  44. 45                         /* negative -> it's going away RSN */
  45. 46         struct completion *pde_unload_completion;
  46. 47         struct list_head pde_openers;   /* who did ->open, but not ->release */
  47. 48         spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
  48. 49         u8 namelen;
  49. 50         char name[];
  50. 51 };
复制代码
挂操作集的方式有使用file_operations和外面的write_proc和read_proc,新版本的内核不提供write_proc/read_proc了。
seq_file的操作方式
  1. struct seq_operations normal_seq_ops =
  2. {
  3.         .start=nor_start,
  4.         .show=nor_show,
  5.         .next=nor_next,
  6.         .stop=nor_stop
  7. };

  8. int normal_open(struct inode *inode, struct file *file)
  9. {
  10.         return seq_open(file,&normal_seq_ops);
  11. }

  12. static struct file_operations fops =
  13. {
  14.         .owner=THIS_MODULE,
  15.         .llseek=seq_lseek,
  16.         .read=seq_read,
  17.         .write=seq_write,
  18.         .open=normal_open,
  19.         .release=seq_release
  20. };
复制代码
使用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的原型为
  1. 35 struct seq_operations {
  2. 36         void * (*start) (struct seq_file *m, loff_t *pos);
  3. 37         void (*stop) (struct seq_file *m, void *v);
  4. 38         void * (*next) (struct seq_file *m, void *v, loff_t *pos);
  5. 39         int (*show) (struct seq_file *m, void *v);
  6. 40 };
复制代码
start返回一个存储了数据的空间,是自己创建的,并在调用show时作为v参数传给show,show负责向用户展示里面的内容。start的*pos是递增的,可以用于判断什么时候返回NULL结束迭代。
show可以对输出内容作格式化处理,参数v含有内容,参数m为输出接口,可使用以下函数
  1. 82 int seq_putc(struct seq_file *m, char c);
  2. 83 int seq_puts(struct seq_file *m, const char *s);

  3. 86 int seq_printf(struct seq_file *, const char *, ...)
复制代码
对于路径及链表,还有其它函数可以使用,详情见内核文档Document/filesystem/seq_file.txt。其中还有一段话,说明其中指向seq_file的指针在调用seq_open的时候被存在struct file的private域中
  1. On a successful open, seq_open() stores the struct seq_file pointer in
  2. file->private_data. If you have an application where the same iterator can
  3. be used for more than one file, you can store an arbitrary pointer in the
  4. private field of the seq_file structure; that value can then be retrieved
  5. by the iterator functions
复制代码
对于2.6.32之类的老内核,可以使用proc_dir_entry里的write_proc和read_proc函数对proc文件进行操作
  1. 46 typedef int (read_proc_t)(char *page, char **start, off_t off,
  2. 47                           int count, int *eof, void *data);
  3. 48 typedef int (write_proc_t)(struct file *file, const char __user *buffer,
  4. 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,不同版本内核不一样


最后附上示例代码:

  1. /*
  2. *创建/proc/net/testdir/目录下ro_read_proc,rw_normal,rw_write_proc三个文件
  3. *rw_normal用seq_file实现,只读
  4. *rw_write_proc用write_proc和read_proc实现,读写,用于输入需要在内核打开的文件名
  5. *ro_read_proc用create_proc_read_entry实现的只读,用打开并输出从rw_write_proc输入的文件名,文件应小于3072字节
  6. */

  7. #include <linux/init.h>
  8. #include <linux/module.h>
  9. #include <linux/proc_fs.h>
  10. #include <linux/fs.h>
  11. #include <linux/seq_file.h>
  12. #include <linux/slab.h>
  13. #include <linux/string.h>
  14. #include <asm/uaccess.h>
  15. #include <net/net_namespace.h> //init_net.proc_net
  16. #include <linux/err.h>        //IS_ERR
  17. #include <linux/dcache.h> //dentry

  18. #include <asm/segment.h>        //use for operating files in kernel space
  19. #include <linux/buffer_head.h>

  20. MODULE_AUTHOR("Liaosp");
  21. MODULE_LICENSE("Dual BSD/GPL");

  22. #define MAXPATHSIZE 256
  23. static char pathbuff[MAXPATHSIZE];

  24. void * nor_start(struct seq_file *m, loff_t *pos)
  25. {
  26. if(*pos>0)
  27. return NULL;
  28. return pathbuff;
  29. }

  30. void nor_stop(struct seq_file *m, void *v)
  31. {
  32. }

  33. void * nor_next(struct seq_file *m, void *v, loff_t *pos)
  34. {
  35. return NULL;
  36. }

  37. int nor_show(struct seq_file *m, void *v)
  38. {
  39. seq_printf(m,"%s",(char *)v);
  40. return 0;
  41. }


  42. struct seq_operations normal_seq_ops =
  43. {
  44. .start=nor_start,
  45. .show=nor_show,
  46. .next=nor_next,
  47. .stop=nor_stop
  48. };

  49. int normal_open(struct inode *inode, struct file *file)
  50. {
  51. return seq_open(file,&normal_seq_ops);
  52. }

  53. static struct file_operations fops =
  54. {
  55. .owner=THIS_MODULE,
  56. .llseek=seq_lseek,
  57. .read=seq_read,
  58. .write=seq_write,           //这里是个严重的BUG,write函数得自己写
  59. .open=normal_open,
  60. .release=seq_release
  61. };

  62. //operating files in kernel
  63. struct file *file_open(const char *path, int flag,int right)
  64. {
  65. mm_segment_t oldfs;
  66. struct file *filp=NULL;
  67. int err=0;

  68. oldfs=get_fs();
  69. set_fs(get_ds());

  70. filp=filp_open(path,flag,right);
  71. if(IS_ERR(filp))
  72. {
  73. printk("open %s error\n",path);
  74. err=PTR_ERR(filp);
  75. return NULL;
  76. }
  77. set_fs(oldfs);
  78. return filp;
  79. }

  80. void file_close(struct file *file)
  81. {
  82. filp_close(file,NULL);
  83. }

  84. int file_read(struct file *file,unsigned char *data, unsigned int size,unsigned long long offset)
  85. {
  86. mm_segment_t oldfs;
  87. int ret;

  88. oldfs=get_fs();
  89. set_fs(get_ds());

  90. ret=vfs_read(file, data, size, &offset);

  91. set_fs(oldfs);
  92. return ret;
  93. }



  94. // what is @start @off @eof @data mean?
  95. static int read_ro(char *page,char **start, off_t off, int count, int *eof, void *date)
  96. {
  97. struct file *fp;
  98. unsigned long re=0;
  99. printk("%s\n",pathbuff);

  100. fp=file_open(pathbuff,O_RDONLY,0);
  101. printk("file open success :%s\n",fp->f_path.dentry->d_name.name);
  102. re=file_read(fp,page,count,off);
  103. file_close(fp);
  104. if(re<=0)

  105. //        re=snprintf(page,10,"in read_ro in ro\n");
  106. *eof=1;
  107. return re;
  108. }
  109. //@page 是内核创建的页
  110. //@start 如果使用自己创建的内存空间,则将指针的地址赋给start
  111. //@off 希望从proc中读开始取的偏移
  112. //@count page页提供的大小
  113. //@eof 当传递结束时,应该把*eof赋1
  114. //@data proc_create_data传的指针
  115. static int rw_r_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
  116. {
  117. int re=0;
  118. printk("in proc_read\noff:%d conut:%d\n",off,count);
  119. re=snprintf(page,MAXPATHSIZE,"%s",pathbuff);
  120. *eof=1;
  121. return re;

  122. }

  123. static int rw_w_proc(struct file *file, const char __user *buffer, unsigned long count, void *data)
  124. {
  125. int re=0;
  126. re=copy_from_user(pathbuff,buffer,count);
  127. pathbuff[count-1]='\0';
  128. printk("%s",pathbuff);
  129. return count;

  130. }

  131. struct proc_dir_entry *dir;        // /proc/net/<dir>

  132. static int exproc_init(void)
  133. {
  134. memset(pathbuff,0,MAXPATHSIZE);
  135. strcpy(pathbuff,"echo the path into this file more len \n");

  136. struct proc_dir_entry *rw_normal; //using file_operations
  137. struct proc_dir_entry *rw_write_proc; //rw using write_proc
  138. struct proc_dir_entry *ro_read_proc; //ro using create_proc_read_entry



  139. //        dir=create_proc_entry("/proc/net/testdir",0666,NULL);
  140. //        don't work

  141. //        struct file *filp = filp_open("/proc/net",O_RDONLY,0);
  142. //        parent=PDE(filp->f_dentry->d_inode);
  143. //        dir=create_proc_entry("testdir",0666,parent);
  144. //        can't work in kernel 2.6.35

  145. dir=proc_mkdir("testdir",init_net.proc_net);
  146. rw_normal=create_proc_entry("rw_normal",0444,dir);
  147. rw_write_proc=create_proc_entry("rw_write_proc",0666,dir);

  148. ro_read_proc=create_proc_read_entry("ro_read_proc",0,dir,read_ro,NULL);

  149. rw_write_proc->read_proc=rw_r_proc;
  150. rw_write_proc->write_proc=rw_w_proc;

  151. rw_normal->proc_fops=&fops;

  152. }
  153. static void exproc_exit(void)
  154. {
  155. remove_proc_entry("rw_normal",dir);
  156. remove_proc_entry("rw_write_proc",dir);
  157. remove_proc_entry("ro_read_proc",dir);
  158. remove_proc_entry("testdir",init_net.proc_net);
  159. }


  160. module_init(exproc_init);
  161. module_exit(exproc_exit);
复制代码
这个文本编辑器真恶心,害我写了两次,还差点要写三次

评分

参与人数 1可用积分 +8 收起 理由
Godbach + 8 赞一个!

查看全部评分

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
2 [报告]
发表于 2016-04-07 16:34 |只看该作者
回复 1# cmqy
感谢分享。

   

论坛徽章:
13
15-16赛季CBA联赛之八一
日期:2016-07-08 21:00:1415-16赛季CBA联赛之同曦
日期:2017-02-15 14:26:1515-16赛季CBA联赛之佛山
日期:2017-02-20 14:19:2615-16赛季CBA联赛之青岛
日期:2017-05-07 16:49:1115-16赛季CBA联赛之广夏
日期:2017-07-30 09:13:1215-16赛季CBA联赛之广东
日期:2018-07-05 22:34:3615-16赛季CBA联赛之江苏
日期:2018-09-03 12:10:2115-16赛季CBA联赛之上海
日期:2018-09-25 03:49:2215-16赛季CBA联赛之广东
日期:2018-09-25 04:09:12
3 [报告]
发表于 2016-04-07 17:00 |只看该作者
大牛!别走!帮我签个名。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP