【驱动】——seq_file使用指南

时间:2022-07-02 03:28:06

  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 查看内核输出信息;

【驱动】——seq_file使用指南

  现在我们把结构体中的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】。