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】。
