转自:http://blog.csdn.net/bullbat/article/details/7407194

版权声明:本文为博主原创文章,未经博主允许不得转载。

作者:bullbat

seq_file机制提供了标准的例程,使得顺序文件的处理好不费力。小的文件系统中的文件,通常用户层是从头到尾读取的,其内容可能是遍历一些数据项创建的。Seq_file机制容许用最小代价实现此类文件,无论名称如何,但顺序文件是可以进行定为操作的,但其实现不怎么高效。顺序访问,即逐个访问读取数据项,显然是首选的访问模式。某个方面具有优势,通常会在其他方面付出代价。

下面我们一步一步来看看怎么编写序列文件的处理程序。对于文件、设备相关驱动程序(其实设备也是文件)的操作,我们都知道需要提供一个struct file_operations的实例。对于这里序列文件的操作,内核中附加提供了一个struct seq_operations结构,该结构很简单:

  1. struct seq_operations {
  2. void * (*start) (struct seq_file *m, loff_t *pos);
  3. void (*stop) (struct seq_file *m, void *v);
  4. void * (*next) (struct seq_file *m, void *v, loff_t *pos);
  5. int (*show) (struct seq_file *m, void *v);
  6. };

start():
         主要实现初始化工作,在遍历一个链接对象开始时,调用。返回一个链接对象的偏移或SEQ_START_TOKEN(表征这是所有循环的开始)。出错返回ERR_PTR。
stop():
        当所有链接对象遍历结束时调用。主要完成一些清理工作。
next():
       用来在遍历中寻找下一个链接对象。返回下一个链接对象或者NULL(遍历结束)。
show():
        对遍历对象进行操作的函数。主要是调用seq_printf(), seq_puts()之类的函数,打印出这个对象节点的信息。

由于c语言中任何数据类型的数据块都可以转化为数据块的内存基址(指针)+数据块大小来传递,不难想到基于我们上面提供的函数,将我们操作的数据用于序列文件的读写、定为、释放等操作完全可以通用话。内核也为我们提供了这些用于读写、定位、释放等操作的通用函数。当然这些操作需要数据结构的支持(比如读取当前位置、数据大小等等),这就是在后面我们会看到的struct seq_file结构。由于我们读写的是文件,在内核中必须提供一个struct file_operations结构的实例,我们可以直接用内核为我们提供的上述函数,并且重写file_operatios结构的open方法,用该方法将虚拟文件系统关联到我们处理的序列文件,那么那些通用的读写函数就可以正常工作了。原理基本上是这样的,下面我们看怎么用file_operatios结构的open方法将我们的序列文件关联到虚拟文件系统。在此之前,我们看看序列文件的表示结构struct seq_file:

  1. struct seq_file {
  2. char *buf;
  3. size_t size;
  4. size_t from;
  5. size_t count;
  6. loff_t index;
  7. loff_t read_pos;
  8. u64 version;
  9. struct mutex lock;
  10. const struct seq_operations *op;
  11. void *private;
  12. };

Buf指向一个内存缓冲区,用于构建传输给用户层的数据。Count指定了需要传输到用户层的剩余的字节数。复制操作的起始位置由from指定,而size给出了缓冲区总的字节数。Index是缓冲区的另一个索引。他标记了内核向缓冲区写入下一个新纪录的起始位置。要注意的是,index和from的演变过程是不同的,因为从内核向缓冲区写入数据,与将这些数据复制到用户空间,这两种操作是不同的。

一般情况,对于序列文件,我们的文件操作实例如下:

  1. static struct file_operations my_operations={
  2. .open   =my_open,
  3. .read   =seq_read,
  4. .llseek =seq_lseek,
  5. .release    =seq_release,
  6. };

其中,my_open函数需要我们重写的,也是我们将其用于关联我们的序列文件。其他都是内核为我们实现好的,在后面我们会详细介绍。

  1. static int my_open(struct inode *inode,struct file *filp)
  2. {
  3. return seq_open(filp,&my_seq_operations);
  4. }

我们这里调用seq_open函数建立这种关联。

  1. int seq_open(struct file *file, const struct seq_operations *op)
  2. {
  3. struct seq_file *p = file->private_data;/*p为seq_file结构实例*/
  4. if (!p) {
  5. p = kmalloc(sizeof(*p), GFP_KERNEL);
  6. if (!p)
  7. return -ENOMEM;
  8. file->private_data = p;/*放到file的private_data中*/
  9. }
  10. memset(p, 0, sizeof(*p));
  11. mutex_init(&p->lock);
  12. p->op = op;/*设置seq_file的operation为op*/
  13. /*
  14. * Wrappers around seq_open(e.g. swaps_open) need to be
  15. * aware of this. If they set f_version themselves, they
  16. * should call seq_open first and then set f_version.
  17. */
  18. file->f_version = 0;
  19. /*
  20. * seq_files support lseek() and pread().  They do not implement
  21. * write() at all, but we clear FMODE_PWRITE here for historical
  22. * reasons.
  23. *
  24. * If a client of seq_files a) implements file.write() and b) wishes to
  25. * support pwrite() then that client will need to implement its own
  26. * file.open() which calls seq_open() and then sets FMODE_PWRITE.
  27. */
  28. file->f_mode &= ~FMODE_PWRITE;
  29. return 0;
  30. }

可以看到,我们的seq_file结构以file的私有数据字段传入虚拟文件系统,同时在open函数中设置了seq_file的操作实例。

我们看下面这个简单的例子:

  1. #include <linux/init.h>
  2. #include <linux/module.h>
  3. #include <linux/kernel.h>
  4. #include <linux/proc_fs.h>
  5. #include <linux/seq_file.h>
  6. #define MAX_SIZE 10
  7. MODULE_LICENSE("GPL");
  8. MODULE_AUTHOR("Mike Feng");
  9. /*用于操作的数据*/
  10. struct my_data
  11. {
  12. int data;
  13. };
  14. /*全局变量*/
  15. struct my_data *md;
  16. /*数据的申请*/
  17. struct my_data* my_data_init(void)
  18. {
  19. int i;
  20. md=(struct my_data*)kmalloc(MAX_SIZE*sizeof(struct my_data),GFP_KERNEL);
  21. for(i=0;i<MAX_SIZE;i++)
  22. (md+i)->data=i;
  23. return md;
  24. }
  25. /*seq的start函数,仅仅做越界判断然后返回pos*/
  26. void *my_seq_start(struct seq_file *file,loff_t *pos)
  27. {
  28. return (*pos<MAX_SIZE)? pos :NULL;
  29. }
  30. /*seq的next函数,仅仅做越界判断然后pos递增*/
  31. void *my_seq_next(struct seq_file *p,void *v,loff_t *pos)
  32. {
  33. (*pos)++;
  34. if(*pos>=MAX_SIZE)
  35. return NULL;
  36. return pos;
  37. }
  38. /*seq的show函数,读数据的显示*/
  39. int my_seq_show(struct seq_file *file,void *v)
  40. {
  41. unsigned int i=*(loff_t*)v;
  42. seq_printf(file,"The %d data is:%d\n",i,(md+i)->data);
  43. return 0;
  44. }
  45. /*seq的stop函数,什么也不做*/
  46. void my_seq_stop(struct seq_file *file,void *v)
  47. {
  48. }
  49. /*operations of seq_file */
  50. static const struct seq_operations my_seq_ops={
  51. .start  =my_seq_start,
  52. .next   =my_seq_next,
  53. .stop   =my_seq_stop,
  54. .show   =my_seq_show,
  55. };
  56. /*file的open函数,用于seq文件与虚拟文件联系*/
  57. static int my_open(struct inode *inode,struct file *filp)
  58. {
  59. return seq_open(filp,&my_seq_ops);
  60. }
  61. /*file操作*/
  62. static const struct file_operations my_file_ops={
  63. .open   =my_open,
  64. .read   =seq_read,
  65. .llseek =seq_lseek,
  66. .release=seq_release,
  67. .owner  =THIS_MODULE,
  68. };
  69. static __init int my_seq_init(void)
  70. {
  71. struct proc_dir_entry *p;
  72. my_data_init();
  73. p=create_proc_entry("my_seq",0,NULL);
  74. if(p)
  75. {
  76. p->proc_fops=&my_file_ops;
  77. }
  78. return 0;
  79. }
  80. static void my_seq_exit(void)
  81. {
  82. remove_proc_entry("my_seq",NULL);
  83. }
  84. module_init(my_seq_init);
  85. module_exit(my_seq_exit);

实验与结果:

你可能会好奇,上面的结果是怎么得到的。当我们用命令cat /proc/my_seq时,即是读取文件/proc/my_seq,而在我们的程序中,my_seq文件绑定到了我们给定的文件操作(p->proc_fops=&my_file_ops;)。那么很自然想到,他是调用my_file_ops中的.read函数,即seq_read函数,我们看看这个函数在内核中是怎么实现的(<fs/seq_file.c>)。

ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)

{

struct seq_file *m = (struct seq_file *)file->private_data;

……

/* we need at least one record in buffer */

pos = m->index;

p = m->op->start(m, &pos);

while (1) {

err = PTR_ERR(p);

if (!p || IS_ERR(p))

break;

err = m->op->show(m, p);

if (err < 0)

break;

if (unlikely(err))

m->count = 0;

if (unlikely(!m->count)) {

p = m->op->next(m, p, &pos);

m->index = pos;

continue;

}

if (m->count < m->size)

goto Fill;

m->op->stop(m, p);

kfree(m->buf);

m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);

if (!m->buf)

goto Enomem;

m->count = 0;

m->version = 0;

pos = m->index;

p = m->op->start(m, &pos);

}

m->op->stop(m, p);

m->count = 0;

goto Done;

……

}

该函数代码比较长,我们只看while循环部分,也即循环打印的过程,我们从红色代码部分可以看出程序循环调用seq_file操作的start、show、next、stop函数,直到读完数据。而start返回的值传入了next和stop函数(就是我们的序列文件读指针索引,在next中为void*类型)。

除了上面的描述,内核还为我们提供了一系列辅助函数,比如single_open函数只需要我们重写show函数即可,需要用的话可以查看相关的代码,了解其定义。这里,我们看看对于内核链表组织的数据seq_file是怎么使用的。

程序文件(list_seq.c):

  1. #include <linux/init.h>
  2. #include <linux/module.h>
  3. #include <linux/kernel.h>
  4. #include <linux/mutex.h>
  5. #include <linux/proc_fs.h>
  6. #include <linux/seq_file.h>
  7. #define N 10
  8. MODULE_LICENSE("GPL");
  9. MODULE_AUTHOR("Mike Feng");
  10. /*对内核链表操作需要加锁*/
  11. static struct mutex lock;
  12. static struct list_head head;
  13. struct my_data
  14. {
  15. struct list_head list;
  16. int value;
  17. };
  18. /*链表的插入元素*/
  19. struct list_head* insert_list(struct list_head *head,int value)
  20. {
  21. struct my_data *md=NULL;
  22. mutex_lock(&lock);
  23. md=(struct my_data*)kmalloc(sizeof(struct my_data),GFP_KERNEL);
  24. if(md)
  25. {
  26. md->value=value;
  27. list_add(&md->list,head);
  28. }
  29. mutex_unlock(&lock);
  30. return head;
  31. }
  32. /*打印,传入参数v为open函数返回的,链表需要操作的节点*/
  33. static int list_seq_show(struct seq_file *file,void *v)
  34. {
  35. struct list_head *list=(struct list_head*)v;
  36. struct my_data *md=list_entry(list,struct my_data,list);
  37. seq_printf(file,"The value of my data is:%d\n",md->value);
  38. return 0;
  39. }
  40. static void *list_seq_start(struct seq_file *file,loff_t *pos)
  41. {
  42. /*加锁*/
  43. mutex_lock(&lock);
  44. return seq_list_start(&head,*pos);
  45. }
  46. static void *list_seq_next(struct seq_file *file,void *v,loff_t *pos)
  47. {
  48. return seq_list_next(v,&head,pos);
  49. }
  50. static void list_seq_stop(struct seq_file *file,void *v)
  51. {
  52. /*解锁*/
  53. mutex_unlock(&lock);
  54. }
  55. static struct seq_operations list_seq_ops=
  56. {
  57. .start  =list_seq_start,
  58. .next   =list_seq_next,
  59. .stop   =list_seq_stop,
  60. .show   =list_seq_show,
  61. };
  62. static int list_seq_open(struct inode *inode,struct file *file)
  63. {
  64. return seq_open(file,&list_seq_ops);
  65. }
  66. static struct file_operations my_file_ops=
  67. {
  68. .open   =list_seq_open,
  69. .read   =seq_read,
  70. .write  =seq_write,
  71. .llseek =seq_lseek,
  72. .release=seq_release,
  73. .owner  =THIS_MODULE,
  74. };
  75. static __init int list_seq_init(void)
  76. {
  77. struct proc_dir_entry *entry;
  78. int i;
  79. mutex_init(&lock);
  80. INIT_LIST_HEAD(&head);
  81. for(i=0;i<N;i++)
  82. head=*(insert_list(&head,i));
  83. entry=create_proc_entry("list_seq",0,NULL);
  84. if(entry)
  85. entry->proc_fops=&my_file_ops;
  86. return 0;
  87. }
  88. static void list_seq_exit(void)
  89. {
  90. struct my_data *md=NULL;
  91. remove_proc_entry("list_seq",NULL);
  92. while(!list_empty(&head))
  93. {
  94. md=list_entry((&head)->next,struct my_data,list);
  95. list_del(&md->list);
  96. kfree(md);
  97. }
  98. }
  99. module_init(list_seq_init);
  100. module_exit(list_seq_exit);

测试试验结果:

由于内核函数list_add为前插,所以打出的数据为倒序的。

序列文件的实现基于proc文件系统,下一步将对其进行分析学习。

Linux内核实践之序列文件【转】的更多相关文章

  1. 在windows下解压缩Linux内核源代码出现重复文件原因

    在windows下解压缩Linux内核源代码出现重复文件原因 2009年06月30日 13:35 来源:ChinaUnix博客 作者:embededgood 编辑:周荣茂     原因一.因为在Lin ...

  2. Linux内核 实践二

    实践二 内核模块编译 20135307 张嘉琪 一.实验原理 Linux模块是一些可以作为独立程序来编译的函数和数据类型的集合.之所以提供模块机制,是因为Linux本身是一个单内核.单内核由于所有内容 ...

  3. linux内核驱动中对文件的读写 【转】

    本文转载自:http://blog.chinaunix.net/uid-13059007-id-5766941.html 有时候需要在Linux kernel--大多是在需要调试的驱动程序--中读写文 ...

  4. Linux内核笔记--深入理解文件描述符

    内核版本:linux-2.6.11 文件描述符(file descriptor)在Linux编程里随处可见,设备读写.网络通信.进程通信,fd可谓是关键中的关键. 深入理解可以增加我们使用它的信心. ...

  5. Linux内核访问用户空间文件:get_fs()/set_fs()的使用

    测试环境:Ubuntu 14.04+Kernel 4.4.0-31 关键词:KERNEL_DS.USER_DS.get_fs().set_fs().addr_limit.access_ok. 参考代码 ...

  6. Linux内核0.11 setup文件说明

    一.总体功能介绍 这是关于Linux-kernel-0.11中boot文件夹下setup.s源文件的实现功能的总结说明. setup.s是一个操作系统加载程序,它的主要功能是利用BIOS中断读取机器系 ...

  7. Linux内核0.11 makefile文件说明

    # # if you want the ram-disk device, define this to be the # size in blocks. # 如果要使用 RAM 就定义块的大小(注释掉 ...

  8. Linux内核分析:打开文件描述符实现

    在Linux中每一个进程的数据是存储在一个task_struct结构(定义在sched.h中)中的. struct task_struct { volatile long state; /* -1 u ...

  9. Linux内核0.11 bootsect文件说明

    一.总体功能介绍 这是关于Linux-kernel-0.11中boot文件夹下bootsect.s源文件的说明,其中涉及到了一些基础知识可以参考这两篇文章. 操作系统启动过程 软盘相关知识和通过BIO ...

随机推荐

  1. js调用js的方法

    a.js文件调用b.js文件时,需要在a.js的第一行添加代码 document.write(" <script language=\"javascript\" s ...

  2. Dapper.Extension的基本使用

    前言    上一篇随笔写了Dapper的简单的使用,这次写一下Dapper.Extension的使用,它是Dapper的简单的封装扩展,可以通过实例化的对象赋值后进行增删改的操作以及分页,但是却不能进 ...

  3. 03019_过滤器Filter

    1.Filter的简介 (1)Filter是对客户端访问资源的过滤,符合条件放行,不符合条件不放行,并且可以对目标资源访问前后进行逻辑处理: (2)快速入门步骤 ①编写一个过滤器的类实现Filter接 ...

  4. unbantu安装wmvare

    最新评论 wsmyyjie:写的太好了!!! zhangmin92:回复 wopapa523: 这个是你用另一.. wopapa523:请问i11是在哪里输入的? myh65013:挺深入的 andk ...

  5. PQFitness

    <html> <head> <title></title> <style> *{margin:o;padding:0;} #header{ ...

  6. 关于safaire下hash前面需要加/(正斜杠)

    就是我们1.1框架是根据hash跳转的,今天我2.0跳转到1.1,pc一直测下来都是好的, 然后发现手机上一直跳转有问题,然后排查了半小时左右才发现  hash前面需要加/ 分割. 例如:http:/ ...

  7. 剑指Offer - 九度1389 - 变态跳台阶

    剑指Offer - 九度1389 - 变态跳台阶2013-11-24 04:20 题目描述: 一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级.求该青蛙跳上一个n级的台阶总共有多少种跳 ...

  8. 《Cracking the Coding Interview》——第3章:栈和队列——题目1

    2014-03-18 03:19 题目:用一个数组实现3个栈. 解法: 首先我想过让三个栈动态决定长度.要么左右各一个向中间靠拢,要么三个穿插着,后来都觉得实现起来太复杂,而且思路总有各种功能缺陷,会 ...

  9. Leetcode 661.图片平滑器

    图片平滑器 包含整数的二维矩阵 M 表示一个图片的灰度.你需要设计一个平滑器来让每一个单元的灰度成为平均灰度 (向下舍入) ,平均灰度的计算是周围的8个单元和它本身的值求平均,如果周围的单元格不足八个 ...

  10. linux 环境下mysql忽略大小写

    mysql数据库在window环境下默认是忽略大小写的,而linux环境中则相反,数据库移植过去后可能会影响到应用工程的正常使用. 解决方法: 用root帐号登录后,在/etc/my.cnf 中的[m ...