linux seq_file 接口

linux seq_file 接口,第1张

概述如我们上面提到的, 在 /proc 下的大文件的实现有点麻烦. 一直以来, /proc 方法因为 当输出数量变大时的错误实现变得声名狼藉. 作为一种清理 /proc 代码以及使内核开发 者活得轻松些的方法, 添加了 seq_file 接口. 这个接口提供了简单的一套函数来实现大 内核虚拟文件.   set_file 接口假定你在创建一个虚拟文件, 它涉及一系列的必须返回给用户空间的项. 为使用 s

如我们上面提到的,在 /proc 下的大文件的实现有点麻烦. 一直以来,/proc 方法因为 当输出数量变大时的错误实现变得声名狼藉. 作为一种清理 /proc 代码以及使内核开发 者活得轻松些的方法,添加了 seq_file 接口. 这个接口提供了简单的一套函数来实现大 内核虚拟文件.

 

set_file 接口假定你在创建一个虚拟文件,它涉及一系列的必须返回给用户空间的项. 为使用 seq_file,你必须创建一个简单的 "iterator" 对象,它能在序列里建立一个位 置,向前进,并且输出序列里的一个项. 它可能听起来复杂,但是,实际上,过程非常简 单. 我们一步步来创建 /proc 文件在 scull 驱动里,来展示它是如何做的.

 

第一步,不可避免地,是包含 <linux/seq_file.h>. 接着你必须创建 4 个 iterator 方 法,称为 start,next,stop,和 show.

 

start 方法一直是首先调用. 这个函数的原型是:

 

voID *start(struct seq_file *sfile,loff_t *pos);

 

sfile 参数可以几乎是一直被忽略. pos 是一个整型位置值,指示应当从哪里读. 位置的 解释完全取决于实现; 在结果文件里不需要是一个字节位置. 因为 seq_file 实现典型地 步进一系列感兴趣的项,position 常常被解释为指向序列中下一个项的指针. scull 驱 动解释每个设备作为系列中的一项,因此进入的 pos 简单地是一个 scull_device 数组 的索引. 因此,scull 使用的 start 方法是:

 

static voID *scull_seq_start(struct seq_file *s,loff_t *pos)

{

if (*pos >= scull_nr_devs)

return NulL;  /* No more to read */ return scull_devices + *pos;

}

 

返回值,如果非 NulL,是一个可以被 iterator 实现使用的私有值.

 

next 函数应当移动 iterator 到下一个位置,如果序列里什么都没有剩下就返回 NulL. 这个方法的原型是:

 

voID *next(struct seq_file *sfile,voID *v,loff_t *pos);

 

这里,v 是从前一个对 start 或者 next 的调用返回的 iterator,pos 是文件的当前位 置. next 应当递增有 pos 指向的值; 根据你的 iterator 是如何工作的,你可能(尽管 可能不会)需要递增 pos 不止是 1. 这是 scull 所做的:

 

static voID *scull_seq_next(struct seq_file *s,loff_t *pos)

{

(*pos)++;

if (*pos >= scull_nr_devs) return NulL;

return scull_devices + *pos;

}

 

当内核处理完 iterator,它调用 stop 来清理: voID stop(struct seq_file *sfile,voID *v);

scull 实现没有清理工作要做,所以它的 stop 方法是空的.

 

设计上,值得注意 seq_file 代码在调用 start 和 stop 之间不睡眠或者进行其他非原 子性任务. 你也肯定会看到在调用 start 后马上有一个 stop 调用. 因此,对你的 start 方法来说请求信号量或自旋锁是安全的. 只要你的其他 seq_file 方法是原子的,调用的整个序列是原子的. (如果这一段对你没有意义,在你读了下一章后再回到这.)

 

在这些调用中,内核调用 show 方法来真正输出有用的东西给用户空间. 这个方法的原型 是:

 

int show(struct seq_file *sfile,voID *v);

 

这个方法应当创建序列中由 iterator v 指示的项的输出. 不应当使用 printk,但是; 有一套特殊的用作 seq_file 输出的函数:

 

int seq_printf(struct seq_file *sfile,const char *fmt,...);

 

这是给 seq_file 实现的 printf 对等体; 它采用常用的格式串和附加值参数. 你 必须也将给 show 函数的 set_file 结构传递给它,然而. 如果 seq_printf 返回 非零值,意思是缓存区已填充,输出被丢弃. 大部分实现忽略了返回值,但是.

 

int seq_putc(struct seq_file *sfile,char c);

int seq_puts(struct seq_file *sfile,const char *s); 它们是用户空间 putc 和 puts 函数的对等体.

int seq_escape(struct seq_file *m,const char *s,const char *esc);

 

这个函数是 seq_puts 的对等体,除了 s 中的任何也在 esc 中出现的字符以八进 制格式打印. esc 的一个通用值是"\t\n\\",它使内嵌的空格不会搞乱输出和可能 搞乱 shell 脚本.

 

int seq_path(struct seq_file *sfile,struct vfsmount *m,struct dentry *dentry,char *esc);

 

这个函数能够用来输出和给定命令项关联的文件名子. 它在设备驱动中不可能有用; 我们是为了完整在此包含它.

 

回到我们的例子; 在 scull 使用的 show 方法是:

 

static int scull_seq_show(struct seq_file *s,voID *v)

{

struct scull_dev *dev = (struct scull_dev *) v; struct scull_qset *d;

int i;

 

if (down_interruptible (&dev->sem)) return -ERESTARTSYS;

 

seq_printf(s,"\nDevice %i: qset %i,q %i,sz %li\n",(int) (dev - scull_devices),dev->qset,dev->quantum,dev->size);

 

for (d = dev->data; d; d = d->next) { /* scan the List */ seq_printf(s," item at %p,qset at %p\n",d,d->data);

 

if (d->data && !d->next) /* dump only the last item */

 

for (i = 0; i < dev->qset; i++) { if (d->data[i])

seq_printf(s," % 4i: %8p\n",

i,d->data[i]);

}

}

up(&dev->sem); return 0;

}

 

这里,我们最终解释我们的" iterator" 值,简单地是一个 scull_dev 结构指针.

 

现在已有了一个完整的 iterator *** 作的集合,scull 必须包装起它们,并且连接它们到

/proc 中的一个文件. 第一步是填充一个 seq_operations 结构:

 

static struct seq_operations scull_seq_ops = {

.start = scull_seq_start,

.next = scull_seq_next,

.stop = scull_seq_stop,

.show = scull_seq_show

};

 

有那个结构在,我们必须创建一个内核理解的文件实现. 我们不使用前面描述过的 read_proc 方法; 在使用 seq_file 时,最好在一个稍低的级别上连接到 /proc. 那意味 着创建一个 file_operations 结构(是的,和字符驱动使用的同样结构) 来实现所有内核 需要的 *** 作,来处理文件上的读和移动. 幸运的是,这个任务是简单的. 第一步是创建一 个 open 方法连接文件到 seq_file *** 作:

 

static int scull_proc_open(struct inode *inode,struct file *file)

{

return seq_open(file,&scull_seq_ops);

}

 

调用 seq_open 连接文件结构和我们上面定义的序列 *** 作. 事实证明,open 是我们必须 自己实现的唯一文件 *** 作,因此我们现在可以建立我们的 file_operations 结构:

 

static struct file_operations scull_proc_ops = {

.owner = THIS_MODulE,

.open = scull_proc_open,

.read = seq_read,

.llseek = seq_lseek,

.release = seq_release

};

 

这里我们指定我们自己的 open 方法,但是使用预装好的方法 seq_read,seq_lseek,和 seq_release 给其他.

 

最后的步骤是创建 /proc 中的实际文件:

 

entry = create_proc_entry("scullseq",NulL); if (entry)

entry->proc_fops = &scull_proc_ops;

 

不是使用 create_proc_read_entry,我们调用低层的 create_proc_entry,我们有这个 原型:

 

struct proc_dir_entry *create_proc_entry(const char *name,mode_t mode,struct proc_dir_entry *parent);

 

参数和它们的在 create_proc_read_entry 中的对等体相同: 文件名子,它的位置,以及 父目录.

 

有了上面代码,scull 有一个新的 /proc 入口,看来很象前面的一个. 但是,它是高级 的,因为它不管它的输出有多么大,它正确处理移动,并且通常它是易读和易维护的. 我 们建议使用 seq_file,来实现包含多个非常小数目的输出行数的文件.

总结

以上是内存溢出为你收集整理的linux seq_file 接口全部内容,希望文章能够帮你解决linux seq_file 接口所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/yw/1019105.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-23
下一篇 2022-05-23

发表评论

登录后才能评论

评论列表(0条)

保存