作者
彭东林
pengdonglin137@163.com
平台
Linux-4.14.13
Qemu + vexpress
概述
前面介绍了single_open,下面结合一个简单的demo驱动,学习一下seq_file的用法。
下面是一张示意图:
![](https://i-blog.csdnimg.cn/blog_migrate/b51b953fa81aedae0fc912659c17a4cd.png)
正文
seq_demo驱动里实现了一个简单的链表,在init的时候会依次创建7个节点并加入链表,然后向用户空间导出一个seq_demo的节点,读取这个节点,就会调用seq_file相关函数对链表进行遍历,输出每个节点的相关信息。
一、seq_demo驱动
#include <linux/init.h> #include <linux/module.h> #include <linux/seq_file.h> #include <linux/debugfs.h> #include <linux/fs.h> #include <linux/list.h> #include <linux/slab.h> static struct dentry *seq_demo_dir; static LIST_HEAD(seq_demo_list); static DEFINE_MUTEX(seq_demo_lock); struct seq_demo_node { char name[10]; struct list_head list; }; static void *seq_demo_start(struct seq_file *s, loff_t *pos) { mutex_lock(&seq_demo_lock); return seq_list_start(&seq_demo_list, *pos); } static void *seq_demo_next(struct seq_file *s, void *v, loff_t *pos) { return seq_list_next(v, &seq_demo_list, pos); } static void seq_demo_stop(struct seq_file *s, void *v) { mutex_unlock(&seq_demo_lock); } static int seq_demo_show(struct seq_file *s, void *v) { struct seq_demo_node *node = list_entry(v, struct seq_demo_node, list); seq_printf(s, "name: %s, addr: 0x%p\n", node->name, node); return 0; } static const struct seq_operations seq_demo_ops = { .start = seq_demo_start, .next = seq_demo_next, .stop = seq_demo_stop, .show = seq_demo_show, }; static int seq_demo_open(struct inode *inode, struct file *file) { return seq_open(file, &seq_demo_ops); } static const struct file_operations seq_demo_fops = { .owner = THIS_MODULE, .open = seq_demo_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release, }; static int __init seq_demo_init(void) { int i; struct seq_demo_node *node; for (i = 0; i < 7; i++) { node = kzalloc(sizeof(struct seq_demo_node), GFP_KERNEL); sprintf(node->name, "node%d", i); INIT_LIST_HEAD(&node->list); list_add_tail(&node->list, &seq_demo_list); } seq_demo_dir = debugfs_create_file("seq_demo", 0444, NULL, NULL, &seq_demo_fops); return 0; } static void __exit seq_demo_exit(void) { struct seq_demo_node *node_pos, *node_n; if (seq_demo_dir) { debugfs_remove(seq_demo_dir); list_for_each_entry_safe(node_pos, node_n, &seq_demo_list, list) if (node_pos) { printk("%s: release %s\n", __func__, node_pos->name); kfree(node_pos); } } } module_init(seq_demo_init); module_exit(seq_demo_exit); MODULE_LICENSE("GPL");
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/seq_file.h> 4 #include <linux/debugfs.h> 5 #include <linux/fs.h> 6 #include <linux/list.h> 7 #include <linux/slab.h> 8 9 static struct dentry *seq_demo_dir; 10 static LIST_HEAD(seq_demo_list); 11 static DEFINE_MUTEX(seq_demo_lock); 12 13 struct seq_demo_node { 14 char name[10]; 15 struct list_head list; 16 }; 17 18 static void *seq_demo_start(struct seq_file *s, loff_t *pos) 19 { 20 mutex_lock(&seq_demo_lock); 21 22 return seq_list_start(&seq_demo_list, *pos); 23 } 24 25 static void *seq_demo_next(struct seq_file *s, void *v, loff_t *pos) 26 { 27 return seq_list_next(v, &seq_demo_list, pos); 28 } 29 30 static void seq_demo_stop(struct seq_file *s, void *v) 31 { 32 mutex_unlock(&seq_demo_lock); 33 } 34 35 static int seq_demo_show(struct seq_file *s, void *v) 36 { 37 struct seq_demo_node *node = list_entry(v, struct seq_demo_node, list); 38 39 seq_printf(s, "name: %s, addr: 0x%p\n", node->name, node); 40 41 return 0; 42 } 43 44 static const struct seq_operations seq_demo_ops = { 45 .start = seq_demo_start, 46 .next = seq_demo_next, 47 .stop = seq_demo_stop, 48 .show = seq_demo_show, 49 }; 50 51 static int seq_demo_open(struct inode *inode, struct file *file) 52 { 53 return seq_open(file, &seq_demo_ops); 54 } 55 56 static const struct file_operations seq_demo_fops = { 57 .owner = THIS_MODULE, 58 .open = seq_demo_open, 59 .read = seq_read, 60 .llseek = seq_lseek, 61 .release = seq_release, 62 }; 63 64 static int __init seq_demo_init(void) 65 { 66 int i; 67 struct seq_demo_node *node; 68 69 for (i = 0; i < 7; i++) { 70 node = kzalloc(sizeof(struct seq_demo_node), GFP_KERNEL); 71 sprintf(node->name, "node%d", i); 72 73 INIT_LIST_HEAD(&node->list); 74 list_add_tail(&node->list, &seq_demo_list); 75 } 76 77 seq_demo_dir = debugfs_create_file("seq_demo", 0444, NULL, 78 NULL, &seq_demo_fops); 79 return 0; 80 } 81 82 static void __exit seq_demo_exit(void) 83 { 84 struct seq_demo_node *node_pos, *node_n; 85 86 if (seq_demo_dir) { 87 debugfs_remove(seq_demo_dir); 88 list_for_each_entry_safe(node_pos, node_n, &seq_demo_list, list) 89 if (node_pos) { 90 printk("%s: release %s\n", __func__, node_pos->name); 91 kfree(node_pos); 92 } 93 } 94 } 95 96 module_init(seq_demo_init); 97 module_exit(seq_demo_exit); 98 MODULE_LICENSE("GPL");
下面是运行结果:
[root@vexpress mnt]# cat /d/seq_demo name: node0, addr: 0xef252000 name: node1, addr: 0xef252680 name: node2, addr: 0xef252380 name: node3, addr: 0xef252740 name: node4, addr: 0xef252b00 name: node5, addr: 0xee80e480 name: node6, addr: 0xeeb9fd40 name: node7, addr: 0xeeb9fd00
二、分析
在遍历链表的时候使用seq_file提供的通用接口函数,当然也可以自己实现,只需要遵循如下原则:
start:根据索引编号pos找到对应的node,并返回该node的地址,也就是show和next方法里的v
next:根据当前node的地址和索引编号计算下一个node的地址和索引编号pos,返回值就是下一个节点的地址
show:输出传入的node的信息
stop:如果在start里有加锁,那么在这里需要释放锁
结合上面的驱动分析一下:
seq_list_start:
1 struct list_head *seq_list_start(struct list_head *head, loff_t pos) 2 { 3 struct list_head *lh; 4 5 list_for_each(lh, head) 6 if (pos-- == 0) 7 return lh; 8 9 return NULL; 10 }
遍历链表,寻找索引为pos的项,找到的话,返回地址,否则返回NULL。当返回NULL的时候, stop会被调用。
seq_list_next:
1 struct list_head *seq_list_next(void *v, struct list_head *head, loff_t *ppos) 2 { 3 struct list_head *lh; 4 5 lh = ((struct list_head *)v)->next; 6 ++*ppos; 7 return lh == head ? NULL : lh; 8 }
计算下一项的地址和索引,如果遍历结束,即lh==head,返回NULL,否则返回下一项的地址。当返回NULL的时候,stop会被调用并把缓冲区中的内容吐给用户
未完待续