天天看點

seq_file學習(2)—— seq_file

作者

彭東林

[email protected]

平台

Linux-4.14.13

Qemu + vexpress

概述

前面介紹了single_open,下面結合一個簡單的demo驅動,學習一下seq_file的用法。

下面是一張示意圖:

seq_file學習(2)—— 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會被調用并把緩沖區中的内容吐給使用者

未完待續

繼續閱讀