转载:http://blog.csdn.net/gangyanliang/article/details/7244664

内容简介:

本文主要讲述序列文件(seq_file)接口的内核实现,如何使用它将Linux内核里面常用的数据结构通过文件(主要关注proc文件)导出到 用户空间,最后定义了一些宏以便于编程,减少重复代码。在分析序列文件接口实现的过程中,还连带涉及到一些应用陷阱和避免手段。


序列文件接口

UNIX的世界里,文件是最普通的概念,所以用文件来作为内核和用户空间传递数据的接口也是再普通不过的事情,并且这样的接口对于shell也是相
当友好的,方便管理员通过shell直接管理系统。由于伪文件系统proc文件系统在处理大数据结构(大于一页的数据)方面有比较大的局限性,使得在那种
情况下进行编程特别别扭,很容易导致bug,所以序列文件接口被发明出来,它提供了更加友好的接口,以方便程序员。之所以选择序列文件接口这个名字,应该
是因为它主要用来导出一条条的记录数据。

为了能给大家一个具体形象的认识,我们首先来看一段用序列文件接口通过proc文件导出内核双向循环链接表的实例代码:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h> static struct mutex lock;
static struct list_head head;
struct my_data {
struct list_head list;
int value;
}; static void add_one(void)
{
struct my_data *data; mutex_lock(&lock);
data = kmalloc(sizeof(*data), GFP_KERNEL);
if (data != NULL)
list_add(&data->list, &head);
mutex_unlock(&lock);
} static ssize_t _seq_write(struct file *file, const char __user * buffer,
size_t count, loff_t *ppos)
{
add_one();
return count;
} static int _seq_show(struct seq_file *m, void *p)
{
struct my_data *data = list_entry(p, struct my_data, list); seq_printf(m, "value: %d/n", data->value);
return ;
} static void *_seq_start(struct seq_file *m, loff_t *pos)
{
mutex_lock(&lock);
return seq_list_start(&head, *pos);
} static void *_seq_next(struct seq_file *m, void *p, loff_t *pos)
{
return seq_list_next(p, &head, pos);
} static void _seq_stop(struct seq_file *m, void *p)
{
mutex_unlock(&lock);
} static struct seq_operations _seq_ops = {
.start = _seq_start,
.next = _seq_next,
.stop = _seq_stop,
.show = _seq_show
}; static int _seq_open(struct inode *inode, struct file *file)
{
return seq_open(file, &_seq_ops);
} static struct file_operations _seq_fops = {
.open = _seq_open,
.read = seq_read,
.write = _seq_write,
.llseek = seq_lseek,
.release = seq_release
}; static void clean_all(struct list_head *head)
{
struct my_data *data; while (!list_empty(head)) {
data = list_entry(head->next, struct my_data, list);
list_del(&data->list);
kfree(data);
}
} static int __init init(void)
{
struct proc_dir_entry *entry; mutex_init(&lock);
INIT_LIST_HEAD(&head); add_one();
add_one();
add_one(); entry = create_proc_entry("my_data",S_IWUSR | S_IRUGO, NULL);
if (entry == NULL) {
clean_all(&head);
return -ENOMEM;
}
entry->proc_fops = &_seq_fops; return ;
} static void __exit fini(void)
{
remove_proc_entry("my_data", NULL);
clean_all(&head);
} module_init(init);
module_exit(fini);

Seq_file File System
针对proc文件的不足而诞生了Seq_file。
Seq_file的实现基于proc文件。使用Seq_file,用户必须抽象出一个链接对象,然后可以依次遍历这个链接对象。这个链接对象可以是链表,数组,哈希表等等。



1.编程接口

Seq_file必须实现四个操作函数:start(), next(), show(), stop()。

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():
主要实现初始化工作,在遍历一个链接对象开始时,调用。返回一个链接对象的偏移或者SEQ_START_TOKEN(表征这是所有循环的开始)。出错返回ERR_PTR。
stop():
当所有链接对象遍历结束时调用。主要完成一些清理工作。
next():
用来在遍历中寻找下一个链接对象。返回下一个链接对象或者NULL(遍历结束)。
show():
对遍历对象进行操作的函数。主要是调用seq_printf(), seq_puts()之类的函数,打印出这个对象节点的信息。
下图描述了seq_file函数对一个链表的遍历。

2、重要的数据结构
除了struct seq_operations以外,另一个最重要的数据结构是struct seq_file:

struct seq_file {
char *buf;
size_t size;
size_t from;
size_t count;
loff_t index;
u64 version;
struct mutex lock;
const struct seq_operations *op;
void *private;
};

该结构会在seq_open函数调用中分配,然后作为参数传递给每个seq_file的操作函数。Privat变量可以用来在各个操作函数之间传递参数。

seq_hello.c

#include <net/net_namespace.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h> #define PROC_NAME "test_proc"
#define MAX_LINES 10 typedef struct item
{
unsigned long key;
unsigned char value;
}user_item; user_item items[]; MODULE_AUTHOR("ZHANG JIE:iptabler@mail.com");
MODULE_LICENSE("GPL"); static void *my_seq_start(struct seq_file *s, loff_t *pos)
{
static unsigned long counter = ;
printk(KERN_INFO"Invoke start/n"); if ( *pos == )
{
/* yes => return a non null value to begin the sequence */
return &counter;
}
else
{
/* no => it's the end of the sequence, return end to stop reading */
*pos = ;
return NULL;
}
} static void *my_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
unsigned long *tmp_v = (unsigned long *)v;
if (*pos < MAX_LINES) {
(*tmp_v)++;
(*pos)++;
return tmp_v;
}
else
{
*pos = ;
return NULL;
}
} static void my_seq_stop(struct seq_file *s, void *v)
{
printk("Invoke stop/n");
} static int my_seq_show(struct seq_file *s, void *v)
{
int i;
loff_t *spos = (loff_t *) v;
for (i = ; i < ; i++)
{
items[i].key = *spos;
}
items[].value = '';
items[].value = '';
items[].value = '';
items[].value = '';
seq_printf(s, "%ld=%c,%ld=%c,%ld=%c,%ld=%c;/n", items[].key,
items[].value, items[].key, items[].value, items[].key,
items[].value, items[].key, items[].value);
return ;
} static struct seq_operations my_seq_ops = {
.start = my_seq_start,
.next = my_seq_next,
.stop = my_seq_stop,
.show = my_seq_show
}; static int my_open(struct inode *inode, struct file *file)
{
return seq_open(file, &my_seq_ops);
}; static struct file_operations my_file_ops = {
.owner = THIS_MODULE,
.open = my_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release
}; int init_module(void)
{
struct proc_dir_entry *entry;
entry = create_proc_entry(PROC_NAME, , init_net.proc_net);
if (entry) {
entry->proc_fops = &my_file_ops;
}
printk(KERN_INFO"Initialze /proc/net/test_proc success!/n");
return ;
} void cleanup_module(void)
{
remove_proc_entry(PROC_NAME, init_net.proc_net);
printk(KERN_INFO"Remove /proc/net/test_proc success!/n");
}

Makefile

obj-m := seq_hello.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd) default:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(RM) *.o *.mod.c *.ko *.symvers *.markers *.order

测试

[root@zj:~/Desktop/net/seq]# cat /proc/net/test_proc

完。

序列文件(seq_file)接口的更多相关文章

  1. linux内核seq_file接口

    seq相关头文件linux/seq_file.h,seq相关函数的实现在fs/seq_file.c.seq函数最早是在2001年就引入了,但以前内核中一直用得不多,而到了2.6内核后,许多/proc的 ...

  2. Linux内核学习笔记之seq_file接口创建可读写proc文件

    转自:http://blog.csdn.net/mumufan05/article/details/45803219 学习笔记与个人理解,如有错误,欢迎指正. 温馨提示:建议跟着注释中的编号顺序阅读代 ...

  3. linux seq_file 接口

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

  4. Linux内核实践之序列文件【转】

    转自:http://blog.csdn.net/bullbat/article/details/7407194 版权声明:本文为博主原创文章,未经博主允许不得转载. 作者:bullbat seq_fi ...

  5. JMeter循环读取CSV文件实现接口批量测试

    首先要理解为什么要进行批量测试,当我们在工作中进行接口测试时,项目的接口肯定不止一个,而是很多很多,而且每个接口都需要进行正确参数,错误参数,参数为空,特殊字符等方式来测试接口是否能够正确返回所需的响 ...

  6. hadoop文本转换为序列文件

    在以前使用hadoop的时候因为mahout里面很多都要求输入文件时序列文件,所以涉及到把文本文件转换为序列文件或者序列文件转为文本文件(因为当时要分析mahout的源码,所以就要看到它的输入文件是什 ...

  7. [SequenceFile_1] Hadoop 序列文件

    1. 关于 SequenceFile 对于日志文件来说,纯文本不适合记录二进制类型数据,通过 SequenceFile 为二进制键值对提供了持久的数据结构,将其作为日志文件的存储格式时,可自定义键(L ...

  8. 关于文件的INode与Java中的文件操作接口

    本文由作者周梁伟授权网易云社区发布. 近日做的项目中涉及到多进程共同读写多个文件的问题,文件名和最后修改时间都是可能会被频繁修改的,因而识别文件的唯一性会产生相当的麻烦,于是专门再学习了一下文件系统对 ...

  9. ActiveX的AssemblyInof.cs文件 IObjectSafety  接口

    ActiveX的AssemblyInof.cs文件 IObjectSafety  接口 [Guid("D4176A17-2A33-4903-8F37-9EBDD7CAFFD3"), ...

随机推荐

  1. 【Remove Duplicates from Sorted Array II】cpp

    题目: Follow up for "Remove Duplicates":What if duplicates are allowed at most twice? For ex ...

  2. Mac OS 添加 MySQL 环境变量

    1.打开终端,输入: cd ~ 会进入~文件夹 2.如果 ~ 文件夹中已经存在 .bash_profile 文件,则在 : 后面追加你想要添加的路径, 3. 否则输入:touch .bash_prof ...

  3. backpropagation算法示例

    backpropagation算法示例 下面举个例子,假设在某个mini-batch的有样本X和标签Y,其中\(X\in R^{m\times 2}, Y\in R^{m\times 1}\),现在有 ...

  4. Centos定时自动执行脚本

    检查本机crond的基本情况 1.crond的运行状况 2.crond是否开机自启动 如何将脚本添加进自动运行的时间内 直接编辑 vim /etc/crontab ,默认的文件形式如后面的图:   我 ...

  5. lua源码分析 伪索引

    Lua 提供了一个 注册表, 这是一个预定义出来的表, 可以用来保存任何 C 代码想保存的 Lua 值. 这个表可以用有效伪索引 LUA_REGISTRYINDEX 来定位. 任何 C 库都可以在这张 ...

  6. jQuery动画的hover连续触发动画bug处理

    一.问题 为元素设置hover上实现动画的效果,当鼠标反复快速进入元素时,动画会在鼠标停止后依然执行,导致动画和鼠标的动作不一致. 二.解决方法 要解决这种问题,可以使用jquery的stop()方法 ...

  7. ZOJ 3874 Permutation Graph ——分治 NTT

    发现每一块一定是按照一定的顺序的. 然后与标号无关,并且相同大小的对答案的影响相同. 然后列出递推式,上NTT+分治就可以了. 然后就可以与输入同阶处理答案了. #include <map> ...

  8. Vue的this.$root.Bus.$on事件被多次触发、多次监听的问题

    前端vue项目中,各个组件(非父子关系也可)之间可以通过Bus进行事件通信. main.js中: import Vue from 'vue' const Bus = new Vue(); const ...

  9. vscode设置背景图片

    最近在知乎上看见有人设置了vscode编辑器的背景图片,觉得很新鲜,就尝试以一下,倒是成功了. 1.首先在vscode扩展中,找到background这个插件,快捷键Ctrl+shift+x 2.完成 ...

  10. 【转】手摸手,带你用vue撸后台 系列四(vueAdmin 一个极简的后台基础模板)

    前言 做这个 vueAdmin-template 的主要原因是: vue-element-admin 这个项目的初衷是一个vue的管理后台集成方案,把平时用到的一些组件或者经验分享给大家,同时它也在不 ...