序列文件(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 0;
}

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 0;
}

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[4];

MODULE_AUTHOR("ZHANG JIE:[email protected]");
MODULE_LICENSE("GPL");

static void *my_seq_start(struct seq_file *s, loff_t *pos)
{
        static unsigned long counter = 0;
         printk(KERN_INFO"Invoke start/n");

        if ( *pos == 0 )
        {
                /* 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 = 0;
                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 = 0;
        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 = 0; i < 4; i++)
    {
     items[i].key = *spos;
    }
       items[0].value = ‘0‘;
       items[1].value = ‘1‘;
       items[2].value = ‘2‘;
       items[3].value = ‘3‘;
       seq_printf(s, "%ld=%c,%ld=%c,%ld=%c,%ld=%c;/n", items[0].key,
         items[0].value, items[1].key, items[1].value, items[2].key,
         items[2].value, items[3].key, items[3].value);
        return 0;
}

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, 0, init_net.proc_net);
        if (entry) {
                 entry->proc_fops = &my_file_ops;
        }
         printk(KERN_INFO"Initialze /proc/net/test_proc success!/n");
        return 0;
}

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

测试

[[email protected]:~/Desktop/net/seq]# cat /proc/net/test_proc

完。

时间: 2024-12-09 14:59:14

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

linux seq_file 接口

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

服务器上传文件出现500错误,但是其他不涉及文件的接口均正常

出现的情景描述: 1.有用户报告说注册无法成功,经过前端的盘查发现实在注册的时候必须调用的上传文件的接口A抛出500错误,但不是每次都抛出不过有很大几率抛出500. 2.A接口接受5个参数和一个文件multi类型,至传递前5个参数能够请求到代码,但是传入文件之后不是500错误就是很长时间超时. 3.重启nginx无效,问题依旧.重启fpm无效,问题依旧. 4.机器很久没有启动过了top显示内存占用较高于是重启机器.重启机器问题消失,A接口正常工作,15分钟后再次出现问题且症状依旧. 5.ngin

用30行代码开发一个上传、下载文件的接口

分享一段代码,开发了3个接口: 1.上传文件 2.查看所有文件 3.下载文件 使用python开发,需要安装flask模块,使用pip install flask安装即可,代码量特别少,就能实现,加上注释总共35行代码,特别的简单,代码如下: import flask,os,time from flask import request,send_from_directory,jsonify app = flask.Flask(__name__)#创建一个app,代表这个web服务 @app.ro

Biopython SeqIO 读取序列文件,读取信息,写入序列

Biopython1.序列赋值 转录(反转录) 翻译 反向互补2.读取序列文件,识别序列的属性信息.SeqRecord提供序列及其注释的容器属性:seq :一条生物序列id:基本ID,标识这条序列name:常用分子的名称description:序列分子的描述letter_annotation:是一个有给每个碱基注释的字典,键是注释类型,值是每个残基序列注释的列表annotations:序列附件信息的字典.键是信息的类型,值包含信息features:是SeqFeature对象的列表 2.1 读取序

Hadoop序列文件

1. 关于SequenceFile 对于日志文件来说,纯文本不适合记录二进制类型数据,通过SequenceFile为二进制键值对提供了持久的数据结构,将其作为日志文件的存储格式时,可自定义键(LongWritable)和值(Writeable的实现类)的类型. 多个小文件在进行计算时需要开启很多进程,所以采用容器文件SequenceFile按固定大小将多个小文件包装起来,使存储和处理更高效. 2. SequenceFile说明 SequenceFile序列文件 是由序列化K-V对组成,而K和V即

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

本文由作者周梁伟授权网易云社区发布. 近日做的项目中涉及到多进程共同读写多个文件的问题,文件名和最后修改时间都是可能会被频繁修改的,因而识别文件的唯一性会产生相当的麻烦,于是专门再学习了一下文件系统对文件的组织管理方式. 一.    文件在文件系统中的组织方式 一块物理磁盘可以被分为若干个分区,分区的初始化操作就是在上面建立文件系统,如ext3,ext4,ntfs或fat32等都是文件系统的概念,还有网络文件系统如NFS等.同块磁盘上的不同分区也可以被指定不同的文件系统,文件系统对文件在磁盘上的

开发一个上传/下载文件的接口

实现的功能:1.上传文件                       2.查看所有文件                       3.下载文件 需要使用flask模块,开启web服务,实现接口,这里还是用一个类封装一下,代码如下:

文件操作接口的系统调用分析---SYSCALL_DEFINEx

linux/arch/arm/kernel/call.S ... CALL(sys_read) CALL(sys_write) CALL(sys_open) CALL(sys_close) ... 以read,write接口举栗子: linux/fs/read_write.c SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count) {      struct file *file;      ssize

MyBatis第一个案例的优化,通过映射文件与接口进行绑定

1.创建表emp CREATE DATABASE mybatis; USE mybatis; CREATE TABLE emp( id INT(11) PRIMARY KEY AUTO_INCREMENT, last_name VARCHAR(255), gender CHAR(1), email VARCHAR(255) ); 2.编写javaBean package cn.bdqn.mybatis.been; public class Emp { private Integer id; pr