seq_file 从内核导出数据到用户空间

seq_file

Posted by cslqm on March 16, 2020

参考:https://www.cnblogs.com/pengdonglin137/p/8439777.html

从内核中导出信息到用户空间有很多方法,可以自己去实现file_operations的read函数或者mmap函数,但是这种方法不够简单,而且也会有一些限制,比如一次read读取大于1页时,驱动里就不得不去进行复杂的缓冲区管理。为此,就需要学习一下seq_file的用法,为了更简单和方便,内核提供了single_xxx系列接口,它是对seq_file的进一步封装。

seq_file 示例程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/*
 * Simple demonstration of the seq_file interface.
 *
 * seq.c,v 1.3 2004/09/26 07:02:43 gregkh Exp
 */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/fs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>


MODULE_AUTHOR("Jonathan Corbet");
MODULE_LICENSE("Dual BSD/GPL");



/*
 * The sequence iterator functions.  The position as seen by the
 * filesystem is just the count that we return.
 */
static void *ct_seq_start(struct seq_file *s, loff_t *pos)
{
        loff_t *spos = kmalloc(sizeof(loff_t), GFP_KERNEL);
        if (!spos)
                return NULL;
        *spos = *pos;
        return spos;
}

static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
        loff_t *spos = (loff_t *) v;
        *pos = ++(*spos);
        return spos;
}

static void ct_seq_stop(struct seq_file *s, void *v)
{
        kfree(v);
}

/*
 * The show function.
 */
static int ct_seq_show(struct seq_file *s, void *v)
{
        loff_t *spos = (loff_t *) v;
        seq_printf(s, "%lld\n", *spos);
        return 0;
}

/*
 * Tie them all together into a set of seq_operations.
 */
static const struct seq_operations ct_seq_ops = {
        .start = ct_seq_start,
        .next  = ct_seq_next,
        .stop  = ct_seq_stop,
        .show  = ct_seq_show
};


/*
 * Time to set up the file operations for our /proc file.  In this case,
 * all we need is an open function which sets up the sequence ops.
 */

static int ct_open(struct inode *inode, struct file *file)
{
        return seq_open(file, &ct_seq_ops);
};

/*
 * The file operations structure contains our open function along with
 * set of the canned seq_ ops.
 */
static const struct file_operations ct_file_ops = {
        .owner   = THIS_MODULE,
        .open    = ct_open,
        .read    = seq_read,
        .llseek  = seq_lseek,
        .release = seq_release
};


/*
 * Module setup and teardown.
 */

static int ct_init(void)
{
        struct proc_dir_entry *entry;

        entry = proc_create("sequence", 0, NULL, &ct_file_ops);
        if (!entry)
                return -ENOMEM;
        return 0;
}

static void ct_exit(void)
{
        remove_proc_entry("sequence", NULL);
}

module_init(ct_init);
module_exit(ct_exit);

single_** 示例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <linux/init.h>
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <linux/fs.h>

static struct dentry *seq_file_demo_dir;

static int seq_file_demo_show(struct seq_file *seq, void *v)
{
        seq_printf(seq, "Hello World\n");
        return 0;
}

static int seq_file_demo_open(struct inode *inode, struct file *file)
{
        return single_open(file, &seq_file_demo_show, NULL);
}

static const struct file_operations seq_file_demo_fops = {
        .owner = THIS_MODULE,
        .open = seq_file_demo_open,
        .read = seq_read,
        .llseek = seq_lseek,
        .release = single_release,
};

static int __init seq_file_demo_init(void)
{
        seq_file_demo_dir = debugfs_create_file("seq_file_demo", 0444, NULL,
                NULL, &seq_file_demo_fops);
        return 0;
}

static void __exit seq_file_demo_exit(void)
{
        if (seq_file_demo_dir)
                debugfs_remove(seq_file_demo_dir);
}

module_init(seq_file_demo_init);
module_exit(seq_file_demo_exit);
MODULE_LICENSE("GPL");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
int single_open(struct file *file, int (*show)(struct seq_file *, void *),
        void *data)
{
    struct seq_operations *op = kmalloc(sizeof(*op), GFP_KERNEL);
    int res = -ENOMEM;

    if (op) {
        op->start = single_start;
        op->next = single_next;
        op->stop = single_stop;
        op->show = show;
        res = seq_open(file, op);
        if (!res)
            ((struct seq_file *)file->private_data)->private = data;
        else
            kfree(op);
    }
    return res;
}

static void *single_start(struct seq_file *p, loff_t *pos)
{
    return NULL + (*pos == 0);
}

static void *single_next(struct seq_file *p, void *v, loff_t *pos)
{
    ++*pos;
    return NULL;
}

static void single_stop(struct seq_file *p, void *v)
{
}

其中,*ops表示的是要输出的元素的索引编号,从0开始,依次递增;

single_start的返回值表示要输出的元素的首地址,这个函数的作用是找到索引号为*pos的元素,并返回该元素的首地址,此外也可以做一些加锁的操作

single_next的入参中v表示刚刚show过的元素的首地址,*pos是该元素的索引,这个函数的目的是计算并返回下一个要show的元素的首地址以及索引号

single_stop里可以做一些释放锁的操作

show需要自己实现,向用户show出当前元素的相关信息.