一个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条件之内的内容不会打印了。因为条件已经不满足了。