歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux內核 >> Linux內核實踐之序列文件

Linux內核實踐之序列文件

日期:2017/3/1 10:25:37   编辑: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是緩沖區的另一個索引。他標記了內核向緩沖區寫入下一個新紀錄的起始位置。要注意的是,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);

實驗與結果:

Copyright © Linux教程網 All Rights Reserved