使用seq_file
在《使用procfs》一文的源码示例中有说到proc文件系统每次读取的数据只能是1个页,如果超过则需多次读取,这样的话会增加读取次数,增多系统调用次数,影响了整体的效率,故而才有seq file序列文件的出现,该项功能使得内核对于大文件的读取更加容易。
对于seq file,其结构体定义在include/linux/seq_file.h文件中,内容如下:
对于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结构体的成员,该结构体在同一头文件中有如下定义:
对于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函数,其定义如下:
通过上面这样粗略的说明大概的结构体操作关联后,最重要的还是读设备结点时的操作,这就依赖于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
/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
评论
发表评论