【驱动】——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】。