Linux中seq_file的应用

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值