【驅動】——seq_file使用指南


  seq_file只是在普通的文件read中加入了內核緩沖的功能,從而實現順序多次遍歷,讀取大數據量的簡單接口。seq_file一般只提供只讀接口,在使用seq_file操作時,主要靠下述四個操作來完成內核自定義緩沖區的遍歷的輸出操作,其中pos作為遍歷的iterator,在seq_read函數中被多次使用,用以定位當前從內核自定義鏈表中讀取的當前位置,當多次讀取時,pos非常重要,且pos總是遵循從0,1,2...end+1遍歷的次序,其即必須作為遍歷內核自定義鏈表的下標,也可以作為返回內容的標識。但是我在使用中僅僅將其作為返回內容的標示,並沒有將其作為遍歷鏈表的下標,從而導致返回數據量大時造成莫名奇妙的錯誤,注意:start返回的void*v如果非0,被show輸出后,在作為參數傳遞給next函數,next可以對其修改,也可以忽略;當next或者start返回NULL時,在seq_open中控制路徑到達seq_end。

struct seq_operations {
    void * (*start) (struct seq_file *m, loff_t *pos);
    void (*stop) (struct seq_file *m, void *v);
    void * (*next) (struct seq_file *m, void *v, loff_t *pos);
    int (*show) (struct seq_file *m, void *v);
};

  start方法始終會首先調用;

  next函數應將迭代器移動到下一個位置,並在序列中沒有其他項目時返回NULL;

  stop做清除工作;

  在上述調用之間,內核會調用 show 方法來將實際的數據輸出到用戶空間。需要使用如下一組特殊函數來處理數據:

int seq_printf(struct seq_file *sfile, const char *fmt, ...);
int seq_putc(struct seq_file *sfile, char c);
int seq_puts(struct seq_file *sfile, const char *s);

  值得注意的是,在設計上,seq_file的代碼不會在 start 和 stop 的調用之間執行其他的非原子操作。我們可以確信,start調用之后馬上就會有對stop的調用。因此在start方法中獲取信號量或者自旋鎖是安全的。

  定義了完整的操作函數,我們必須將這些函數打包並和 /proc 中的某個文件連接起來。首先要填充一個 seq_operations 結構:

static struct seq_operations seq_ops = {
    .start = seq_start,
    .next = seq_next,
    .stop = seq_stop,
    .show = seq_show 
};

  有了這個結構我們可以創建一個open方法,將文件連接到seq_file操作:

static int proc_open(struct inode *inode, struct file *file){
    return seq_open(file, &scull_seq_ops);
}

  對seq_open的調用將file結構和我們上面定義的順序操作連接在一起。open是唯一一個必須由我們自己實現的文件操作,因此我們的file_operations結構可如下定義:

static struct file_operations proc_ops = {
    .owner = THIS_MODULE,
    .open = proc_open,
    .read = seq_read,
    .llseek = seq_lseek,
    .release = seq_release
};

  這里,我們指定了我們自己的open方法,但對其他的file_operations成員,我們使用了已經定義好的 seq_read, seq_lseek, seq_release方法。  

  最后,我們建立實際的 /proc 文件;

  entry = create_porc_entyr("sequence", 0, NULL);

  if (entry)

    entry->proc_fops = &scull_proc_ops;

例一:現在我們使用seq_file獲取0-100的數:

 1 #include <linux/init.h>
 2 #include <linux/module.h>
 3 #include <linux/types.h>
 4 #include <linux/fs.h>
 5 #include <linux/cdev.h>
 6 #include <linux/kernel.h>
 7 #include <asm/uaccess.h>
 8 #include <linux/seq_file.h>
 9 #include <linux/proc_fs.h>
10 
11 #define MAX_NUM     100
12 static void *ct_seq_start(struct seq_file *s, loff_t *pos){ 
13     loff_t *spos = kmalloc(sizeof(loff_t), GFP_KERNEL);
14 
15     if (*pos > MAX_NUM)
16         return NULL;
17 
18     if (!*spos){
19         return NULL;
20     }   
21     *spos = *pos;
22     return spos;
23 }  
24 static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos){
25     loff_t *spos = (loff_t *)v;
26 
27     *pos = ++(*spos);
28     if (*pos > MAX_NUM){
29         return NULL;
30     }
31     return spos;
32 }
33 
34 static void ct_seq_stop(struct seq_file *s, void *v){
35     printk("ct_seq_stop!\n");
36     kfree(v);
37 }
38 
39 static int ct_seq_show(struct seq_file *s, void *v){
40     loff_t *spos = (loff_t *)v;
41     seq_printf(s, "%lld\n", *spos);
42     return 0;
43 }
44 static const struct seq_operations ct_seq_ops = {
45     .start = ct_seq_start,
46     .next  = ct_seq_next,
47     .stop  = ct_seq_stop,
48     .show  = ct_seq_show
49 };
50 
51 static int ct_open(struct inode *inode, struct file *file){
52     printk("ct_open!\n");
53     return seq_open(file, &ct_seq_ops);
54 }
55 
56 static const struct file_operations ct_file_ops = {
57     .owner   = THIS_MODULE,
58     .open    = ct_open,
59     .read    = seq_read,
60     .llseek  = seq_lseek,
61     .release = seq_release
62 };
63 static int ct_init(void){
64     struct proc_dir_entry *entry;
65     entry = create_proc_entry("sequence", 0, NULL);
66     if (entry)
67         entry->proc_fops = &ct_file_ops;
68     return 0;
69 }
70 static void ct_exit(void){
71     remove_proc_entry("sequence", NULL);
72 }
73 module_init(ct_init);
74 module_exit(ct_exit);
75 
76 MODULE_LICENSE("GPL");

  cat /proc/sequence 遍可以獲取0-100的數字;

例二:使用seq_file獲取鏈表中的數據:

  1 #include <linux/init.h>
  2 #include <linux/module.h>
  3 #include <linux/types.h>
  4 #include <linux/fs.h>
  5 #include <linux/cdev.h>
  6 #include <linux/kernel.h>
  7 #include <asm/uaccess.h>
  8 #include <linux/seq_file.h>
  9 #include <linux/proc_fs.h>
 10 #include <linux/string.h>
 11 
 12 #define MAX_NUM     5
 13 struct node {
 14     char buf[10];
 15     struct node *next;
 16 };
 17 struct node *head = NULL;
 18 static int init_data(void){
 19     int i;
 20     struct node *tmp = NULL;
 21     char ch = 'a';
 22     for (i = 0; i < MAX_NUM; i++){
 23         tmp = (struct node *)kmalloc(sizeof(struct node), GFP_KERNEL);
 24         if (!tmp){
 25             return -ENOMEM;
 26         }
 27         memset(tmp->buf, 0, sizeof(tmp->buf));
 28         memset(tmp->buf, ch + i, sizeof(tmp->buf) - 1);
 29         tmp->next = NULL;
 30         if (!head){
 31             head = tmp;
 32         }
 33         else {
 34             tmp->next = head;
 35             head = tmp;
 36         }
 37     }
 38     return 0;
 39 }
 40 static int free_data(void){
 41     struct node *tmp = NULL;
 42 
 43     while (head){
 44         tmp = head;
 45         head = head->next;
 46         kfree(tmp);
 47     }
 48     return 0;
 49 }
 50 
 51 static void *ct_seq_start(struct seq_file *s, loff_t *pos){
 52     struct node *tmp = head;
 53     int index = *pos + 1;
 54 
 55     printk("seq start!\n");
 56     if (!*pos){
 57         return head;
 58     }
 59     while (index--){
 60         if (!tmp){
 61             return NULL;
 62         }
 63         tmp = tmp->next;
 64     }
 65 
 66     return tmp;
 67 }
 68 
 69 static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos){
 70     struct node *tmp = (struct node *)v;
 71 
 72     printk("seq next!\n");
 73     *pos = *pos + 1;
 74     tmp = tmp->next;
 75     if (!tmp){
 76         return NULL;
 77     }
 78 
 79     return tmp;
 80 }
 81 
 82 static void ct_seq_stop(struct seq_file *s, void *v){
 83     printk("ct_seq_stop!\n");
 84 }
 85 static int ct_seq_show(struct seq_file *s, void *v){
 86     struct node *tmp = (struct node *)v;
 87     printk("seq show!\n");
 88     seq_printf(s, "%s\n", tmp->buf);
 89     return 0;
 90 }
 91 
 92 static const struct seq_operations ct_seq_ops = {
 93     .start = ct_seq_start,
 94     .next  = ct_seq_next,
 95     .stop  = ct_seq_stop,
 96     .show  = ct_seq_show
 97 };
 98 
 99 static int ct_open(struct inode *inode, struct file *file){
100     printk("ct_open!\n");
101     return seq_open(file, &ct_seq_ops);
102 }
103 
104 static const struct file_operations ct_file_ops = {
105     .owner   = THIS_MODULE,
106     .open    = ct_open,
107     .read    = seq_read,
108     .llseek  = seq_lseek,
109     .release = seq_release
110 };
111 
112 static int ct_init(void){
113     struct proc_dir_entry *entry;
114     init_data();
115     entry = create_proc_entry("sequence", 0, NULL);
116     if (entry)
117         entry->proc_fops = &ct_file_ops;
118     return 0;
119 }
120 static void ct_exit(void){
121     free_data();
122     remove_proc_entry("sequence", NULL);
123 }
124 module_init(ct_init);
125 module_exit(ct_exit);
126 
127 MODULE_LICENSE("GPL");

  cat /proc/sequence 會把鏈表中的數據輸出,此時我們使用 dmesg 查看內核輸出信息;

  現在我們把結構體中的buf數據變成1024,MAX_NUM改為10,然后執行 cat /proc/sequence 然后再 dmesg 看看;

結果為:

ct_open!
seq start!
seq show!
seq next!
seq show!
seq next!
seq show!
seq next!
seq show!
ct_seq_stop!
seq start!
seq show!
seq next!
seq show!
seq next!
seq show!
seq next!
seq show!
ct_seq_stop!
seq start!
seq show!
seq next!
seq show!
seq next!
seq show!
seq next!
ct_seq_stop!
seq start!
ct_seq_stop!

  我們發現中間執行了 stop ,而不是像上面一樣執行 start->show->next->show->next->stop。原因就是某次調用 show 的時候發現 seq_file 中的buf滿了【buf——4K】。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM