linux seq_file 接口
如我们上面提到的, 在 /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, void *v, 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", 0,
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接口创建可读写proc文件
转自:http://blog.csdn.net/mumufan05/article/details/45803219 学习笔记与个人理解,如有错误,欢迎指正. 温馨提示:建议跟着注释中的编号顺序阅读代 ...
- linux内核seq_file接口
seq相关头文件linux/seq_file.h,seq相关函数的实现在fs/seq_file.c.seq函数最早是在2001年就引入了,但以前内核中一直用得不多,而到了2.6内核后,许多/proc的 ...
- 序列文件(seq_file)接口
转载:http://blog.csdn.net/gangyanliang/article/details/7244664 内容简介: 本文主要讲述序列文件(seq_file)接口的内核实现,如何使用它 ...
- Linux驱动程序接口
§1. Linux驱动程序接口 系统调用是操作系统内核与应用程序之间的接口,设备驱动程序则是操作系统内核与机器硬件的接口.几乎所有的系统操作最终映射到物理设备,除了CPU.内存和少数其它设备,所有的设 ...
- linux rtc 接口【转】
转自:http://blog.csdn.net/goldfighter/article/details/6126178 Linux操作系统内核对RTC的编程详解 转自: http://xenyinze ...
- [Linux] Linux smaps接口文件结构
在Linux内核 2.6.16中引入了一个系统内存接口特性,这个接口位于/proc/$pid/目录下的smaps文件中 ,一看内容发现是进程内存映像信息,比同一目录下的maps文件更详细些. 400d ...
- Linux系统调用接口添加简单示例
1. Linux体系结构 Linux系统的地址空间分为用户空间和内核空间,通过系统调用和硬件中断能够完成从用户空间到内核空间的转移. 2. 系统调用接口 ① 一般情况下,用户进程不能访问内核空间.Li ...
- linux alloc_pages 接口
为完整起见, 我们介绍另一个内存分配的接口, 尽管我们不会准备使用它直到 15 章. 现 在, 能够说 struct page 是一个描述一个内存页的内部内核结构. 如同我们将见到的, 在内核中有许多 ...
- Linux内核接口特定的类型
内核中一些通常使用的数据类型有它们自己的 typedef 语句, 因此阻止了任何移植性问 题. 例如, 一个进程标识符 ( pid ) 常常是 pid_t 而不是 int. 使用 pid_t 屏蔽了任 ...
随机推荐
- JQuery-- 链式编程、静态函数,自己制作jQuery插件
一.链式编程 为什么jQuery运行链式编程 ,让我们的代码(方法)连续不间断书写(连续调用)其实主要还是jQuery很多的函数执行完毕之后,都会返回一个jQuery对象 因为获取操作的时候,会返回获 ...
- Linux C socket 基于 UDP
/************************************************************************* > File Name: serve ...
- 【转载】【软件安装】Source Insight 4.0常用设置
1.Source Insight简介 Source Insight是一个面向软件开发的代码编辑器和浏览器,它拥有内置的对C/C++, C#和Java等源码的分析,创建并动态维护符号数据库,并自动显示有 ...
- 【51NOD1304】字符串的相似度
题目描述 我们定义2个字符串的相似度等于两个串的相同前缀的长度.例如 "abc" 同 "abd" 的相似度为2,"aaa" 同 " ...
- 【JZOJ4791】【NOIP2016提高A组模拟9.21】矩阵
题目描述 在麦克雷的面前出现了一个有n*m个格子的矩阵,每个格子用"."或"#"表示,"."表示这个格子可以放东西,"#" ...
- 【Leetcode栈】有效的括号(20)
题目 给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效. 有效字符串需满足: 1,左括号必须用相同类型的右括号闭合. 2,左括号必须以正确的顺序闭合. 注意 ...
- 小爬爬4:selenium操作
1.selenium是什么? selenium: - 概念:是一个基于浏览器自动化的模块. - 和爬虫之间的关联? - 帮我我们便捷的爬取到页面中动态加载出来的数据 - 实现模拟登陆 - 基本使用流程 ...
- pytest笔记
-v 参数显示执行过程 测试覆盖率: ldy@ldy-D214:~/workspace/socai$ pipenv run pytest tests/unit/test_models.py --cov ...
- 04使用harbor配置私仓
安装harbor之前,需要安装好Python,Docker,DockerCompose.Python需要2.7以上的版本,Docker需要1.10以上的版本:Docker Compose 需要1.6. ...
- js获取当前日期及获取当前日期的前一天日期函数
function getcurrentdate(){ //获取系统时间var LSTR_ndate=new Date();var LSTR_Year=LSTR_ndate.getFullYear(); ...