使用seq_file

在《使用procfs》一文的源码示例中有说到proc文件系统每次读取的数据只能是1个页,如果超过则需多次读取,这样的话会增加读取次数,增多系统调用次数,影响了整体的效率,故而才有seq file序列文件的出现,该项功能使得内核对于大文件的读取更加容易。
对于seq file,其结构体定义在include/linux/seq_file.h文件中,内容如下:
struct seq_file {
        char *buf;
        size_t size;
        size_t from;
        size_t count;
        size_t pad_until;
        loff_t index;
        loff_t read_pos;
        u64 version;
        struct mutex lock;
        const struct seq_operations *op;
        int poll_event;
        #ifdef CONFIG_USER_NS
                struct user_namespace *user_ns;
        #endif
        void *private;
};
这里不详细说明该结构体,仅供感受下。
对于seq_file,相应的文件操作需要实现seq_operations结构体的成员,该结构体在同一头文件中有如下定义:
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);
};
实现该结构体成员后,需要使用seq_open打开,让文件与该seq_operations关联起来,最终这个open函数要作为file_operations结构体里面的open成员,对于这个file_operations,我们还是要依赖学习procfs时的proc_create来将其与procfs里的文件节点关联。
通过上面这样粗略的说明大概的结构体操作关联后,最重要的还是读设备结点时的操作,这就依赖于seq_open关联的seq_operations结构体的4个成员了,通过fs/seq_file.c文件里的seq_read函数定义,可以了解到这4个成员的执行顺序,其先执行start成员函数,然后执行show成员函数,接下来判断next是否为空,不为空时则show下next,如果为空则调用stop结束读操作。
在读操作时会用到fs/seq_file.c文件里的seq_printf函数,其定义如下:
int seq_printf(struct seq_file *m, const char *f, ...)
{
        int ret;
        va_list args;

        va_start(args, f);
        ret = seq_vprintf(m, f, args);
        va_end(args);

        return ret;
}
至此,基本内容讲了一番,不过这样的操作对于大于页面大小的操作更合适些,如果是对于一页或更小的读操作,显然只需要show这一函数操作即可了,seq_file也提供了相应的操作,在fs/seq_file.c文件中有如下定义,这里只把函数头部摘抄如下:
int single_open(struct file *file, int (*show)(struct seq_file *, void *), void *data) ;
从该文件可以看到其实现,仅保留show为我们自定义的,其他3个则为内定的,只是对pos进行操作。看到了吧,我们只需将上面的seq_open替换为single_open且只定义show函数即可。相应的删除卸载函数就不多说了,直接上代码吧:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>

struct proc_dir_entry * slam_entry = NULL;
struct proc_dir_entry * single_slam_entry = NULL;

static char * entry_name = "slam";
static char * single_entry_name = "single_slam";

#define MAX_SLAM_SIZE 10

static int slam[MAX_SLAM_SIZE];

static void * slam_start(struct seq_file *m, loff_t *pos)
{
        return (*pos < MAX_SLAM_SIZE) ? pos : NULL;
}

static int slam_show(struct seq_file *m, void *p)
{
        unsigned int i = *(loff_t *)p;
        seq_printf(m, "The %d data is : %d\n", i, slam[i]);
        return 0;
}

static void * slam_next(struct seq_file *m, void *p, loff_t *pos)
{
        (*pos)++;
        if (*pos >= MAX_SLAM_SIZE)
                return NULL;
        return pos;
}

static void slam_stop(struct seq_file *m, void *p)
{
}

static int single_slam_show(struct seq_file *m, void *p)
{
        seq_printf(m, "%s\n", "In single slam show!");
        return 0;
}

static struct seq_operations slam_seq_op =
{
        .start = slam_start,
        .next = slam_next,
        .stop = slam_stop,
        .show = slam_show,
};

static int slam_open(struct inode *inode, struct file *file)
{
        return seq_open(file, &slam_seq_op);
}

static int single_slam_open(struct inode *inode, struct file *file)
{
        return single_open(file, single_slam_show, NULL);
}

static const struct file_operations slam_fops =
{
        .owner = THIS_MODULE,
        .open = slam_open,
        .read = seq_read,
        .llseek = seq_lseek,
        .release = seq_release,
};

static const struct file_operations single_slam_fops =
{
        .owner = THIS_MODULE,
        .open = single_slam_open,
        .read = seq_read,
        .llseek = seq_lseek,
        .release = single_release,

};

static __init int seqfile_example_init(void)
{
        #ifdef CONFIG_PROC_FS
                int i;
                for (i = 0; i < MAX_SLAM_SIZE; i++)
                {
                        slam[i] = 2 * i + 1;
                }

                slam_entry = proc_create(entry_name, 0666, NULL, &slam_fops);
                if (!slam_entry)
                {
                        printk("Create file \"%s\" failed.\n", entry_name);
                        return -1;
                }

                single_slam_entry = proc_create(single_entry_name, 0666, NULL, &single_slam_fops);
                if (!single_slam_entry)
                {
                        printk("Create file \"%s\" failed.\n", single_entry_name);
                        return -1;
                }

        #else
                printk("This module requests the kernel to support procfs,need set CONFIG_PROC_FS configure Y\n");
        #endif
        return 0;
}

static __exit void seqfile_example_exit(void)
{
        #ifdef CONFIG_PROC_FS
                proc_remove(single_slam_entry);
                proc_remove(slam_entry);
        #endif
}

module_init(seqfile_example_init);
module_exit(seqfile_example_exit);
相应的Makefile内容如下:
obj-m += seqfile_example.o

CUR_PATH:=$(shell pwd)
LINUX_KERNEL_PATH:=/home/xinu/linux-3.13.6

all:
        make -C $(LINUX_KERNEL_PATH) M=$(CUR_PATH) modules

clean:
        make -C $(LINUX_KERNEL_PATH) M=$(CUR_PATH) clean
相应的源码文件目录树如下:
/home/xinu/xinu/linux_kernel_driver_l1/seqfile_example/
├── Makefile
└── seqfile_example.c
剩下的就是编译加载KO,然后使用cat /proc/slam和cat /proc/single_slam看看吧!
参考网址:
http://www.embeddedlinux.org.cn/html/yingjianqudong/201304/17-2551.html
https://www.ibm.com/developerworks/cn/linux/l-kerns-usrs2/
http://blog.csdn.net/tommy_wxie/article/details/7576046
http://nano-chicken.blogspot.com/2009/12/linux-modulesiv-seqfile.html
http://nano-chicken.blogspot.com/2009/12/linux-modulesiv-1-seqfilefpprocdevicesc.html
http://nano-chicken.blogspot.com/2009/12/linux-modulesiv-2-seqfilesingle-page.html

评论

此博客中的热门博文

I/O映射之I/O端口

通过Netlink检测网线插拔