您的位置:首页 > 运维架构 > Linux

Linux中seq_file的应用

2016-06-28 14:09 471 查看
seq_file和seq_operations结构体定义在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;
};

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_file接口,首先需要定义一个file_operations结构体,以实现open方法:
static const struct file_operations debug_dump_info_fops = {
.owner		= THIS_MODULE,
.open		= debugfs_dump_info_open,
.read		= seq_read,
.release	= single_release,
};
debugfs_dump_info_open定义:
static int debugfs_dump_info_open(struct inode *inode, struct file *file)
{
return single_open(file, msg21xx_debug_dump_info, inode->i_private);
}
inode->i_private中保存了驱动的私有数据结构地址,msg21xx_debug_dump_info函数就是seq_file的show方法,内核在设计上,seq_file的代码不会在start和stop调用之间执行其他的非原子操作,内核在调用start之后,马上就会调用stop,所以在start方法中获取信号量或者自旋锁是安全的,在调用start和stop之间,内核会调用show方法即msg21xx_debug_dump_info将实际的数据输出到用户空间,msg21xx_debug_dump_info调用seq_printf函数将data->ts_info指向的数据输出到m所指向的buf中,seq_printf会调用seq_vprintf,seq_vprintf调用vsnprintf将数据输出到buf中。
static int msg21xx_debug_dump_info(struct seq_file *m, void *v)
{
struct msg21xx_ts_data *data = m->private;

seq_printf(m, "%s\n", data->ts_info);

return 0;
}

seq_printf和seq_vprintf均定义在fs/seq_file.c文件中:
int seq_vprintf(struct seq_file *m, const char *f, va_list args)
{
int len;

if (m->count < m->size) {
len = vsnprintf(m->buf + m->count, m->size - m->count, f, args);
if (m->count + len < m->size) {
m->count += len;
return 0;
}
}
seq_set_overflow(m);
return -1;
}
EXPORT_SYMBOL(seq_vprintf);

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;
}
EXPORT_SYMBOL(seq_printf);debugfs_dump_info_open函数调用single_open来初始化seq_operations中的start,stop,next,show函数指针。
int single_open(struct file *file, int (*show)(struct seq_file *, void *),
void *data)
{
struct seq_operations *op = kmalloc(sizeof(*op), GFP_KERNEL);
int res = -ENOMEM;

if (op) {
op->start = single_start;
op->next = single_next;
op->stop = single_stop;
op->show = show;
res = seq_open(file, op);
if (!res)
((struct seq_file *)file->private_data)->private = data;
else
kfree(op);
}
return res;
}
seq_open中分配seq_file结构体,通过file->private_data = p;将file的private_data指针指向分配的seq_file结构,再通过single_open函数中的((struct seq_file *)file->private_data)->private = data;将驱动的私有数据结构的地址赋值给seq_file结构的private指针,这样一来,在msg21xx_debug_dump_info函数中,就可以通过struct msg21xx_ts_data
*data = m->private;获得驱动的数据结构地址。
/**
*	seq_open -	initialize sequential file
*	@file: file we initialize
*	@op: method table describing the sequence
*
*	seq_open() sets @file, associating it with a sequence described
*	by @op.  @op->start() sets the iterator up and returns the first
*	element of sequence. @op->stop() shuts it down.  @op->next()
*	returns the next element of sequence.  @op->show() prints element
*	into the buffer.  In case of error ->start() and ->next() return
*	ERR_PTR(error).  In the end of sequence they return %NULL. ->show()
*	returns 0 in case of success and negative number in case of error.
*	Returning SEQ_SKIP means "discard this element and move on".
*/
int seq_open(struct file *file, const struct seq_operations *op)
{
struct seq_file *p = file->private_data;

if (!p) {
p = kmalloc(sizeof(*p), GFP_KERNEL);
if (!p)
return -ENOMEM;
file->private_data = p;
}
memset(p, 0, sizeof(*p));
mutex_init(&p->lock);
p->op = op;
#ifdef CONFIG_USER_NS
p->user_ns = file->f_cred->user_ns;
#endif

/*
* Wrappers around seq_open(e.g. swaps_open) need to be
* aware of this. If they set f_version themselves, they
* should call seq_open first and then set f_version.
*/
file->f_version = 0;

/*
* seq_files support lseek() and pread().  They do not implement
* write() at all, but we clear FMODE_PWRITE here for historical
* reasons.
*
* If a client of seq_files a) implements file.write() and b) wishes to
* support pwrite() then that client will need to implement its own
* file.open() which calls seq_open() and then sets FMODE_PWRITE.
*/
file->f_mode &= ~FMODE_PWRITE;
return 0;
}
最后只需调用debugfs_create_file完成所有的工作,并将驱动的私有数据结构指针传递到struct inode结构的inode->i_private。
temp = debugfs_create_file("dump_info", S_IRUSR | S_IWUSR,
data->dir,
data, &debug_dump_info_fops);
if (temp == NULL || IS_ERR(temp)) {
pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp));
err = PTR_ERR(temp);
goto free_debug_dir;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: