在上面我们介绍了使用 seq_file 需要实现的一些函数和相关结构体,现在我们把它们组合起来,介绍以下
 通过 proc 来使用 seq_file 的一般步骤,而 seq_file 在其他方面的应用方法也是一样的。
 (1) 实现 seq_operations ,也就是前面我们介绍的 seq_file 的底层数据操作函数集,示例如下:
 static struct seq_operations proc_seq_ops = {
 .start = proc_seq_start,
 .next = proc_seq_next,
 .stop = proc_seq_stop,
 .show = proc_seq_show
 };
 这些回调函数都是需要根据你所要获取的序列数据结构来实现的。
 (2) 实现 struct file_operations 中的 open 函数,这个 open 函数的实现也是非常简单固定格式,无私有数据
 情况实例如下:
 static int proc_seq_open(struct inode *inode, struct file *file)
 {
 return seq_open(file, &proc_seq_ops);
 };
 seq_open 的实现如下,此函数实现在 seq_file.c 中。
 int seq_open(struct file *file, const struct seq_operations *op)
struct seq_file *p;
 WARN_ON(file->private_data);
 p = kzalloc(sizeof(*p), GFP_KERNEL);
 if (!p)
 return -ENOMEM;
 file->private_data = p;
 mutex_init(&p->lock);
 p->op = op;
 // No refcounting: the lifetime of 'p' is constrained
 // to the lifetime of the file.
 p->file = file;
 /*
 * Wrappers around seq_open(e.g. swaps_open) need to be
 * aware of this. If they set f_version themselves, they
 * should call seq_open first and then set f_version.
 */
 file->f_version = 0;
 /*
 * seq_files support lseek() and pread(). They do not implement
 * write() at all, but we clear FMODE_PWRITE here for historical
 * reasons.
 *
 * If a client of seq_files a) implements file.write() and b) wishes to
 * support pwrite() then that client will need to implement its own
 * file.open() which calls seq_open() and then sets FMODE_PWRITE.
 */
 file->f_mode &= ~FMODE_PWRITE;
 return 0;
 }
 seq_file 结构会在 seq_open 中申请,并附在 file 的私有数据成员 private_data 中。可以看到,一般就是使用
 seq_file 中的一个 API:seq_open,目的是向 seq_file 结构体中注册一个 struct seq_operations 。
 另外还有 int seq_open_private(struct file *filp, const struct seq_operations *ops,int psize)函数,此函数会为
 seq_file 的私有数据申请空间。
 若需要私有数据可使用 seq_open_private 接口。
 (3) 实现 struct file_operations proc_ops,示例如下:
 static struct file_operations proc_ops = {
 .owner = THIS_MODULE,
 .open = proc_seq_open,
 .read = seq_read,
 .llseek = seq_lseek,
 .release = seq_release,
 };
 大家可以看到,以上的 read、llseek、release 都是有 seq_file 提供的接口函数,直接注册即可,唯有 open 是
 需要自己实现的。
在 seq 中是没有实现 write 操作的,因此,如果根据需要添加 write 操作,需要再 file_ops 中单独添加。
 (4)注册 proc 文件,注册包含 seq_file 操作函数的 file_operations 结构体到 proc_fops。我的测试程序中的实
 例代码如下:
 ……
 proc_test_entry = create_proc_entry("proc_seq", 0644, NULL);
 if (proc_test_entry == NULL) {
 ret = -ENOMEM;
 cleanup_test_data();
 pr_err("proc_test: Couldn't create proc entry\n");
 } else {
 proc_test_entry->proc_fops = &proc_ops;
 pr_info("proc_test: Module loaded.\n");
 }
示例
#include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/kernel_stat.h>
 #include <linux/fs.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #define MAX_CPU_NUM 32
 #define RPT_LINE_MAXLEN 1024
 unsigned int last_user[MAX_CPU_NUM];
 unsigned int last_system[MAX_CPU_NUM];
 unsigned int last_nice[MAX_CPU_NUM];
 unsigned int last_idle[MAX_CPU_NUM];
 unsigned int last_si[MAX_CPU_NUM];
 unsigned int last_hi[MAX_CPU_NUM];
 static struct proc_dir_entry *monitor_root_dir;
 static struct proc_dir_entry *proc_tos;
 #define LEFT(x) (((unsigned)x) / 10)
 #define RIGHT(x) (((unsigned)x) % 10)
 #define SEQ_START_TOKEN ((void *)1)
 static void *tos_get_cpuinfo_start(struct seq_file *f, loff_t *pos)
 {
 void *v = NULL;
 v = *pos ? pos : SEQ_START_TOKEN;
 return v;
 }
 static int tos_get_cpuinfo_show(struct seq_file *f, void *v)
 {
 unsigned int user=0, system=0, nice=0, idle=0, si = 0, hi = 0;
unsigned int sum;
 int cpu_num = num_possible_cpus();
 int i;
 if (v == SEQ_START_TOKEN){
 for(i=0;i<cpu_num;i++){
 user = kcpustat_cpu(i).cpustat[CPUTIME_USER]-last_user[i];
 nice = kcpustat_cpu(i).cpustat[CPUTIME_NICE]-last_nice[i];
 system = kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]-last_system[i];
 idle = kcpustat_cpu(i).cpustat[CPUTIME_IDLE]-last_idle[i];
 si = kcpustat_cpu(i).cpustat[CPUTIME_SOFTIRQ]-last_si[i];
 hi = kcpustat_cpu(i).cpustat[CPUTIME_IRQ]-last_hi[i];
 sum = user+nice+system+idle + si + hi;
 if(sum==0){
 user=1;
 nice = 0;
 system =89;
 idle = 10;
 si = 0;
 hi = 0;
 sum = user+nice+system+idle + si + hi;
 }
 seq_printf(f, "CPU Load
 Information:(%d %s)\n",cpu_num,cpu_num>1?"cpus":"cpu");
 seq_printf(f," cpu%d:\n",i);
 seq_printf(f, "%-18s%u.%u%%\n", " User",
 LEFT(user*1000/sum),
 RIGHT(user*1000/sum));
 seq_printf(f, "%-18s%u.%u%%\n", " System",
 LEFT(system*1000/sum),
 RIGHT(system*1000/sum));
 seq_printf(f, "%-18s%u.%u%%\n"," Nice",
 LEFT(nice*1000/sum),
 RIGHT(nice*1000/sum));
 seq_printf(f, "%-18s%u.%u%%\n"," Si",
 LEFT(si*1000/sum),
 RIGHT(si*1000/sum));
 seq_printf(f, "%-18s%u.%u%%\n"," Hi",
 LEFT(hi*1000/sum),
 RIGHT(hi*1000/sum));
 seq_printf(f, "%-18s%u.%u%%\n"," Idle",
 LEFT(idle*1000/sum),
 RIGHT(idle*1000/sum));
 }
 seq_printf(f, "\n");
}
 return 0;
 }
 static void *tos_get_cpuinfo_next(struct seq_file *f, void *v, loff_t *pos)
 {
 (*pos)++;
 return v == SEQ_START_TOKEN ? NULL : NULL;//只获取一次
 }
 static void tos_get_cpuinfo_stop(struct seq_file *f, void *v)
 {
 /* Nothing to do */
 }
 static const struct seq_operations tos_get_cpuinfo_sops = {
 .start = tos_get_cpuinfo_start,
 .next = tos_get_cpuinfo_next,
 .stop = tos_get_cpuinfo_stop,
 .show = tos_get_cpuinfo_show
 };
 static int tos_get_cpuinfo_open(struct inode *inode, struct file *filp)
 {
 return seq_open(filp, &tos_get_cpuinfo_sops);
 }
 static const struct file_operations tos_get_cpuinfo_fopes = {
 .open = tos_get_cpuinfo_open,
 .read = seq_read,
 .llseek = seq_lseek,
 .release = seq_release,
 };
 static int __init tos_get_cpuinfo_init(void)
 {
 static struct proc_dir_entry *entry;
 proc_tos = proc_mkdir("tos", 0);
 if (!proc_tos) {
 printk("tos proc mkdir tos failed!\n");
 return -1;
 }
 monitor_root_dir = proc_mkdir("tos/monitor", NULL);
 if(monitor_root_dir == NULL){
 printk("tos/monitor proc mkdir tos failed!\n");
 if(proc_tos)
 remove_proc_entry("tos", NULL);
 return -ENOMEM;
 }
 entry = proc_create("cpu", 0644, monitor_root_dir, &tos_get_cpuinfo_fopes);
 if(!entry) {
 printk("create_proc_entry error");
if(monitor_root_dir)
 remove_proc_entry("tos/monitor", NULL);
 if(proc_tos)
 remove_proc_entry("tos", NULL);
 return -1;
 }
 return 0;
 }
 static void __exit tos_get_cpuinfo_exit(void)
 {
 remove_proc_entry("cpu", monitor_root_dir);
 if(monitor_root_dir)
 remove_proc_entry("tos/monitor", NULL);
 if(proc_tos)
 remove_proc_entry("tos", NULL);
 }
 MODULE_LICENSE("GPL");
 module_init(tos_get_cpuinfo_init);
 module_exit(tos_get_cpuinfo_exit);
 复杂的示例可阅读内核源码 fib_trie.c,查看路由的 proc 文件的实现,其中遍历路由树时使用的自己的迭代
 器,迭代器在 seq->private 记录。