作者
彭東林
平台
Linux-4.14.13
Qemu + vexpress
概述
前面介紹了single_open,下面結合一個簡單的demo驅動,學習一下seq_file的用法。
下面是一張示意圖:
正文
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);
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會被調用并把緩沖區中的内容吐給使用者
未完待續