• 一个seq_file的小问题


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

    static void flowinfo_seq_printf_stats(struct seq_file *seq, struct xxx *pxxx)
    {
        seq_printf(seq, "
    flow alloc info in cpuindex %d:
    ", 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
    ",dump_flow);
        seq_printf(seq, "
    flow alloc info in cpuindex %d:
    ", 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条件之内的内容不会打印了。因为条件已经不满足了。

    
    
     
    水平有限,如果有错误,请帮忙提醒我。如果您觉得本文对您有帮助,可以点击下面的 推荐 支持一下我。版权所有,需要转发请带上本文源地址,博客一直在更新,欢迎 关注 。
  • 相关阅读:
    update数据从一个表到另外一个表中
    数据泵导出
    导入库
    看函数
    导库中的一个表
    一个表的字段存在或者不存在另一表里
    语句2
    语句
    word 内容被锁定,无法修改
    gridview自带分页
  • 原文地址:https://www.cnblogs.com/10087622blog/p/9431515.html
Copyright © 2020-2023  润新知