安庆

导航

一个seq_file的小问题

在修改一个内核模块的时候,我们使用seq_file来打印我们的数据,结果非常出人意料。

static void flowinfo_seq_printf_stats(struct seq_file *seq, struct xxx *pxxx)
{
    seq_printf(seq, "\nflow alloc info in cpuindex %d:\n", cpuindex);
    if ( dump_flow >= 0) 
    {
          dump_flow =-1;
          其他的很多seq_printf
.......
}
还有很多seq_printf
}

dump_flow 是一个全局变量,初始化为-1.

使用echo方式修改 dump_flow 。

但是当我使用echo 赋值 dump_flow =1之后,我使用cat还能查看到 dump_flow 的值修改为1了,也就是确认了这个值已经按要求修改为了预期的值。

接下来,我执行 cat 动作来触发 flowinfo_seq_show 函数的调用
static const struct seq_operations flowinfo_seq_ops = {
    .start = flowinfo_seq_start,
    .next  = flowinfo_seq_next,
    .stop  = flowinfo_seq_stop,
    .show  = flowinfo_seq_show,


static int flowinfo_seq_show(struct seq_file *seq, void *v)
{
    if (v == SEQ_START_TOKEN)
        flowinfo_seq_printf_stats(seq, v);
    else {}
    return 0;
}

  static int flowinfo_seq_open(struct inode *inode, struct file *file)
  {
        return seq_open(file, &flowinfo_seq_ops);;
  }

  static const struct file_operations flowinfo_seq_fops = {
    .owner = THIS_MODULE,
    .open = flowinfo_seq_open,
    .read = seq_read,
    .llseek = seq_lseek,
    .release = flowinfo_seq_release,

  

很悲剧的是,我并没有看到我原本在if条件里面应该输出的 seq里面的内容。

很确定的是,flowinfo_seq_show  肯定是调用了,为啥没看到内容呢?

我修改一下我的show函数:

static void flowinfo_seq_printf_stats(struct seq_file *seq, struct witdriver *witdriver)
{
   printk("dump_flow =%d\n",dump_flow);
    seq_printf(seq, "\nflow alloc info in cpuindex %d:\n", cpuindex);
    if ( dump_flow >= 0) 
    {
          dump_flow =-1;
          其他的很多seq_printf
....... }
更多的seq_printf 调用。。。。。。。。
}  

 

结果看到,dmesg中的内容如下:

dump_flow=1
dump_flow=-1
dump_flow=-1
dump_flow=-1

 dump_flow被打印了四次,我只执行了一次cat,为啥show函数会执行四次呢?

最终通过走查代码,发现密码是存在于:seq_read 函数中。

ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
	struct seq_file *m = file->private_data;
	size_t copied = 0;
	loff_t pos;
	size_t n;
	void *p;
	int err = 0;

	mutex_lock(&m->lock);

	/*
	 * seq_file->op->..m_start/m_stop/m_next may do special actions
	 * or optimisations based on the file->f_version, so we want to
	 * pass the file->f_version to those methods.
	 *
	 * seq_file->version is just copy of f_version, and seq_file
	 * methods can treat it simply as file version.
	 * It is copied in first and copied out after all operations.
	 * It is convenient to have it as  part of structure to avoid the
	 * need of passing another argument to all the seq_file methods.
	 */
	m->version = file->f_version;

	/*
	 * if request is to read from zero offset, reset iterator to first
	 * record as it might have been already advanced by previous requests
	 */
	if (*ppos == 0)
		m->index = 0;

	/* Don't assume *ppos is where we left it */
	if (unlikely(*ppos != m->read_pos)) {
		while ((err = traverse(m, *ppos)) == -EAGAIN)
			;
		if (err) {
			/* With prejudice... */
			m->read_pos = 0;
			m->version = 0;
			m->index = 0;
			m->count = 0;
			goto Done;
		} else {
			m->read_pos = *ppos;
		}
	}

	/* grab buffer if we didn't have one */
	if (!m->buf) {
		m->buf = seq_buf_alloc(m->size = PAGE_SIZE);
		if (!m->buf)
			goto Enomem;
	}
	/* if not empty - flush it first */
	if (m->count) {
		n = min(m->count, size);
		err = copy_to_user(buf, m->buf + m->from, n);
		if (err)
			goto Efault;
		m->count -= n;
		m->from += n;
		size -= n;
		buf += n;
		copied += n;
		if (!m->count)
			m->index++;
		if (!size)
			goto Done;
	}
	/* we need at least one record in buffer */
	pos = m->index;
	p = m->op->start(m, &pos);-------------这里开始
	while (1) {
		err = PTR_ERR(p);
		if (!p || IS_ERR(p))
			break;
		err = m->op->show(m, p);------------show函数调用,但是此时没有fill到用户态
		if (err < 0)
			break;
		if (unlikely(err))
			m->count = 0;
		if (unlikely(!m->count)) {
			p = m->op->next(m, p, &pos);
			m->index = pos;
			continue;
		}
		if (m->count < m->size)------------由于这个条件不满足,所以依然不会fill
			goto Fill;
		m->op->stop(m, p);-----------------m->count >=m->size,将执行stop,
		seq_buf_free(m->buf);--------------释放原本的buf
		m->count = 0;
		m->buf = seq_buf_alloc(m->size <<= 1);----再次申请m->size ,size扩大一倍
		if (!m->buf)
			goto Enomem;
		m->version = 0;
		pos = m->index;
		p = m->op->start(m, &pos);---------重新来过,再次循环
	}
	m->op->stop(m, p);
	m->count = 0;
	goto Done;
Fill:
	/* they want more? let's try to get some more */
	while (m->count < size) {
		size_t offs = m->count;
		loff_t next = pos;
		p = m->op->next(m, p, &next);
		if (!p || IS_ERR(p)) {
			err = PTR_ERR(p);
			break;
		}
		err = m->op->show(m, p);
		if (seq_has_overflowed(m) || err) {
			m->count = offs;
			if (likely(err <= 0))
				break;
		}
		pos = next;
	}
	m->op->stop(m, p);
	n = min(m->count, size);
	err = copy_to_user(buf, m->buf, n);
	if (err)
		goto Efault;
	copied += n;
	m->count -= n;
	if (m->count)
		m->from = n;
	else
		pos++;
	m->index = pos;
Done:
	if (!copied)
		copied = err;
	else {
		*ppos += copied;
		m->read_pos += copied;
	}
	file->f_version = m->version;
	mutex_unlock(&m->lock);
	return copied;
Enomem:
	err = -ENOMEM;
	goto Done;
Efault:
	err = -EFAULT;
	goto Done;
}

 由于seq要打印内容过多,所以会导致我们填充到m->buf的数据并没有fill到用户态,而是释放掉buf,然后扩大buf到原来的两倍,直到能够满足我们seq要输出的内容为止。

所以,我的show函数调用了好几遍,当第二遍之后,我的 dump_flow  已经被改成了-1,由于buf还是不够,又来了第三遍,第四遍,不过后面的几遍中,if条件之内的内容不会打印了。因为条件已经不满足了。

 


 

posted on 2018-08-06 17:32  _备忘录  阅读(389)  评论(0编辑  收藏  举报