在《使用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
网友评论