序列文件(seq_file)接口
转载: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)接口的更多相关文章
- linux内核seq_file接口
seq相关头文件linux/seq_file.h,seq相关函数的实现在fs/seq_file.c.seq函数最早是在2001年就引入了,但以前内核中一直用得不多,而到了2.6内核后,许多/proc的 ...
- Linux内核学习笔记之seq_file接口创建可读写proc文件
转自:http://blog.csdn.net/mumufan05/article/details/45803219 学习笔记与个人理解,如有错误,欢迎指正. 温馨提示:建议跟着注释中的编号顺序阅读代 ...
- linux seq_file 接口
如我们上面提到的, 在 /proc 下的大文件的实现有点麻烦. 一直以来, /proc 方法因为 当输出数量变大时的错误实现变得声名狼藉. 作为一种清理 /proc 代码以及使内核开发 者活得轻松些的 ...
- Linux内核实践之序列文件【转】
转自:http://blog.csdn.net/bullbat/article/details/7407194 版权声明:本文为博主原创文章,未经博主允许不得转载. 作者:bullbat seq_fi ...
- JMeter循环读取CSV文件实现接口批量测试
首先要理解为什么要进行批量测试,当我们在工作中进行接口测试时,项目的接口肯定不止一个,而是很多很多,而且每个接口都需要进行正确参数,错误参数,参数为空,特殊字符等方式来测试接口是否能够正确返回所需的响 ...
- hadoop文本转换为序列文件
在以前使用hadoop的时候因为mahout里面很多都要求输入文件时序列文件,所以涉及到把文本文件转换为序列文件或者序列文件转为文本文件(因为当时要分析mahout的源码,所以就要看到它的输入文件是什 ...
- [SequenceFile_1] Hadoop 序列文件
1. 关于 SequenceFile 对于日志文件来说,纯文本不适合记录二进制类型数据,通过 SequenceFile 为二进制键值对提供了持久的数据结构,将其作为日志文件的存储格式时,可自定义键(L ...
- 关于文件的INode与Java中的文件操作接口
本文由作者周梁伟授权网易云社区发布. 近日做的项目中涉及到多进程共同读写多个文件的问题,文件名和最后修改时间都是可能会被频繁修改的,因而识别文件的唯一性会产生相当的麻烦,于是专门再学习了一下文件系统对 ...
- ActiveX的AssemblyInof.cs文件 IObjectSafety 接口
ActiveX的AssemblyInof.cs文件 IObjectSafety 接口 [Guid("D4176A17-2A33-4903-8F37-9EBDD7CAFFD3"), ...
随机推荐
- Oracle 学习笔记(十)
合并查询 在实际项目开发中经常遇到要合并结果集的情况,可以使用集合操作符:union,union all,intersect,minus.这次笔记学习这几个操作符. [union] 该操作符用于取得两 ...
- Mac OS 添加 MySQL 环境变量
1.打开终端,输入: cd ~ 会进入~文件夹 2.如果 ~ 文件夹中已经存在 .bash_profile 文件,则在 : 后面追加你想要添加的路径, 3. 否则输入:touch .bash_prof ...
- HDU5857 Median 模拟
Median HDU - 5857 There is a sorted sequence A of length n. Give you m queries, each one contains fo ...
- redhat-2
2016年5月16日-工作日志1 通过PXE安装RHEL7.2系统,部署satellite6.2(采用不是least-stable版本,是Satellite-6.2.0-RHEL-7-20160512 ...
- 打开任意位置的webConfig
请阅读原文:打开任意的配置文件 翻翻ConfigurationManager的签名,有一个方法吸引了我的注意:OpenExeConfiguration(string exePath).看上去我可以把B ...
- koa2在node6中如何运行
koa2在node6下运行 { "babel-core": "^6.24.1", "babel-plugin-syntax-async-functio ...
- 34条简单的SQL优化准则
转载地址:http://bbs.csdn.net/topics/260002113 我们要做到不但会写SQL,还要做到写出性能优良的SQL,以下为笔者学习.摘录.并汇总部分资料与大家分享!(1) ...
- Fragment控件初始化
代码改变世界 Fragment控件初始化 @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup ...
- POJ 2376:Cleaning Shifts(贪心)
题目大意:有n个奶牛,他们负责在长为t个时间点的时间内值班,每个时间点至少有一个在值班,每个奶牛有一段空闲时间可以值班,求满足要求所需最少奶牛数量,无法满足则输出-1. 分析: 将奶牛空闲时间段看成线 ...
- windows部分快捷键及terminal命令
切换任务: win+tab terminal命令 pwd:打印当前工作目录 hostname:获取我的计算机的网络名称 mkdir:创建目录 cd:更改目录 ls/ dir -R:列出目录下的文件 r ...