在修改一个内核模块的时候,我们使用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条件之内的内容不会打印了。因为条件已经不满足了。