Linux内核实践之序列文件


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是缓冲区的另一个索引。他标记了内核向缓冲区写入下一个新纪录的起始位置。要注意的是,indexfrom的演变过程是不同的,因为从内核向缓冲区写入数据,与将这些数据复制到用户空间,这两种操作是不同的。

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

  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.   
  5.     if (!p) {  
  6.         p = kmalloc(sizeof(*p), GFP_KERNEL);  
  7.         if (!p)  
  8.             return -ENOMEM;  
  9.         file->private_data = p;/*放到file的private_data中*/  
  10.     }  
  11.     memset(p, 0, sizeof(*p));  
  12.     mutex_init(&p->lock);  
  13.     p->op = op;/*设置seq_file的operation为op*/  
  14.   
  15.     /* 
  16.      * Wrappers around seq_open(e.g. swaps_open) need to be 
  17.      * aware of this. If they set f_version themselves, they 
  18.      * should call seq_open first and then set f_version. 
  19.      */  
  20.     file->f_version = 0;  
  21.   
  22.     /* 
  23.      * seq_files support lseek() and pread().  They do not implement 
  24.      * write() at all, but we clear FMODE_PWRITE here for historical 
  25.      * reasons. 
  26.      * 
  27.      * If a client of seq_files a) implements file.write() and b) wishes to 
  28.      * support pwrite() then that client will need to implement its own 
  29.      * file.open() which calls seq_open() and then sets FMODE_PWRITE. 
  30.      */  
  31.     file->f_mode &= ~FMODE_PWRITE;  
  32.     return 0;  
  33. }  
可以看到,我们的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.   
  7.   
  8. #define MAX_SIZE 10   
  9.   
  10.   
  11. MODULE_LICENSE("GPL");  
  12. MODULE_AUTHOR("Mike Feng");  
  13.   
  14. /*用于操作的数据*/  
  15. struct my_data  
  16. {  
  17.     int data;  
  18. };  
  19.   
  20. /*全局变量*/  
  21. struct my_data *md;  
  22.   
  23. /*数据的申请*/  
  24. struct my_data* my_data_init(void)  
  25. {  
  26.     int i;  
  27.     md=(struct my_data*)kmalloc(MAX_SIZE*sizeof(struct my_data),GFP_KERNEL);  
  28.   
  29.     for(i=0;i<MAX_SIZE;i++)  
  30.         (md+i)->data=i;  
  31.   
  32.     return md;  
  33. }  
  34.   
  35. /*seq的start函数,仅仅做越界判断然后返回pos*/  
  36. void *my_seq_start(struct seq_file *file,loff_t *pos)  
  37. {  
  38.     return (*pos<MAX_SIZE)? pos :NULL;  
  39. }  
  40.   
  41. /*seq的next函数,仅仅做越界判断然后pos递增*/  
  42. void *my_seq_next(struct seq_file *p,void *v,loff_t *pos)  
  43. {  
  44.     (*pos)++;  
  45.     if(*pos>=MAX_SIZE)  
  46.         return NULL;  
  47.   
  48.     return pos;  
  49. }  
  50.   
  51. /*seq的show函数,读数据的显示*/  
  52. int my_seq_show(struct seq_file *file,void *v)  
  53. {  
  54.     unsigned int i=*(loff_t*)v;  
  55.     seq_printf(file,"The %d data is:%d\n",i,(md+i)->data);  
  56.       
  57.     return 0;  
  58. }  
  59.   
  60. /*seq的stop函数,什么也不做*/  
  61. void my_seq_stop(struct seq_file *file,void *v)  
  62. {  
  63.   
  64. }  
  65.   
  66.   
  67. /*operations of seq_file */  
  68. static const struct seq_operations my_seq_ops={  
  69.     .start  =my_seq_start,  
  70.     .next   =my_seq_next,  
  71.     .stop   =my_seq_stop,  
  72.     .show   =my_seq_show,  
  73. };  
  74.   
  75. /*file的open函数,用于seq文件与虚拟文件联系*/  
  76. static int my_open(struct inode *inode,struct file *filp)  
  77. {  
  78.     return seq_open(filp,&my_seq_ops);  
  79. }  
  80.   
  81. /*file操作*/  
  82. static const struct file_operations my_file_ops={  
  83.     .open   =my_open,  
  84.     .read   =seq_read,  
  85.     .llseek =seq_lseek,  
  86.     .release=seq_release,  
  87.     .owner  =THIS_MODULE,  
  88. };  
  89.   
  90. static __init int my_seq_init(void)  
  91. {  
  92.     struct proc_dir_entry *p;  
  93.     my_data_init();  
  94.     p=create_proc_entry("my_seq",0,NULL);  
  95.     if(p)  
  96.     {  
  97.         p->proc_fops=&my_file_ops;  
  98.     }  
  99.   
  100.     return 0;  
  101.       
  102. }  
  103.   
  104. static void my_seq_exit(void)  
  105. {  
  106.     remove_proc_entry("my_seq",NULL);  
  107. }  
  108.   
  109. module_init(my_seq_init);  
  110. module_exit(my_seq_exit);  

实验与结果:

     

  • 1
  • 2
  • 下一页

相关内容

    暂无相关文章