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


 

一个seq_file的小问题的更多相关文章

  1. 第一个Mac shell 小脚本

    大多数程序员都喜欢偷懒的,我也不例外.相信好多Android开发的coder 在网络http请求方面,会浪费很多时间在接口调试这里..有时候,自己写了一个小测试,行还好,不行的话,还要跟写后台的哥们一 ...

  2. 一个python爬虫小程序

    起因 深夜忽然想下载一点电子书来扩充一下kindle,就想起来python学得太浅,什么“装饰器”啊.“多线程”啊都没有学到. 想到廖雪峰大神的python教程很经典.很著名.就想找找有木有pdf版的 ...

  3. 用原生javascript做的一个打地鼠的小游戏

    学习javascript也有一段时间了,一直以来分享的都是一些概念型的知识,今天有空做了一个打地鼠的小游戏,来跟大家分享一下,大家也可以下载来增加一些生活的乐趣,下面P出代码:首先是HTML部分代码: ...

  4. 今天来做一个PHP电影小爬虫。

    今天来做一个PHP电影小爬虫.我们来利用simple_html_dom的采集数据实例,这是一个PHP的库,上手很容易.simple_html_dom 可以很好的帮助我们利用php解析html文档.通过 ...

  5. 第一个leapmotion的小游戏

    自从看过leapmotion的宣传视频,就被吸引住了.觉得这东西迟早要替代鼠标,然后关注了一年多leapmotion的动态,终于在今年8月份入手了一只.//675大洋啊,心疼~ 一直想写份评测,一直想 ...

  6. 撸一个JS正则小工具

    写完正则在浏览器上检测自己写得对不对实在是不方便,于是就撸了一个JS正则小demo出来. demo demo展示 项目地址 代码部分 首先把布局样式先写好. <!DOCTYPE html> ...

  7. 输出多行字符的一个简单JAVA小程序

    public class JAVA { public static void main(String[] args) { System.out.println("-------------- ...

  8. 【C语言探索之旅】 第一部分第八课:第一个C语言小游戏

    ​ 内容简介 1.课程大纲 2.第一部分第八课:第一个C语言小游戏 3.第一部分第九课预告: 函数 课程大纲 我们的课程分为四大部分,每一个部分结束后都会有练习题,并会公布答案.还会带大家用C语言编写 ...

  9. 一个简洁的小H车调运模型

    一个简洁的小H车调运模型 不久前, 帝都B城市到处都是小H车, 理想的小H车应该是布朗运动\均匀分布,可是现实上它们就是不均匀.于是有如下问题: 观察帝都 HD区SY村区域,将其划分成10个用车点,用 ...

随机推荐

  1. mongodb windows的安装方法和添加到任务管理器中、检测是否成功、增删改查命令

    转: mongodb安装方法: https://blog.csdn.net/heshushun/article/details/77776706        mongodb检测安装成功 .以及增删改 ...

  2. Jmeter(二十七)Jmeter Question 之“集成Ant+Jenkins自动化”

    首先介绍一下Ant.Apache Ant,是一个将软件编译.测试.部署等步骤联系在一起加以自动化的一个工具,大多用于Java环境中的软件开发.由Apache软件基金会所提供. 是的.还是Apache家 ...

  3. STL常用容器使用方法

    在程序头部使用#include<stack>来引入STL的stack容器,然后使用stack<int> s语句来声明一个管理整型数据的容器s.stack常用成员函数:push( ...

  4. HTTP协议的简单解析

    超文本传输协议(HTTP,HyperText Transfer Protocol)是用于从服务器传输超文本到本地浏览器的传输协议,是应用最为广泛的网络协议.B/S网络架构的核心是HTTP,掌握HTTP ...

  5. http note

    http 多媒体传输协议 HyperText Transfer Protocol 超文本传输协议 https Hyper Text Transfer Protocol over Secure Sock ...

  6. c语言数据类型(二)

    char 类型 1.char 变量 常量 char c; 定义一个char变量 c = ‘a’   'a'字符常量 char 的本质就是一个整数,只有一个字节大小的整数 2.printf 输出char ...

  7. Linux最大线程数限制及当前线程数查询

    常用配置 echo > /proc/sys/kernel/pid_max a) 当前环境生效 ulimit -d unlimited ulimit -m unlimited ulimit -s ...

  8. python连接数据库——create_engine和conn.cursor

    python操作数据库的方法: 一种是导入sqlalchemy包,另一种是导入psycopg2包. 具体用法如下(此处以postgre数据库举例) 第一种: # 导入包 from sqlalchemy ...

  9. vmware centos7 动态ip->静态

    TYPE=Ethernet BOOTPROTO=none DEFROUTE=yes IPV4_FAILURE_FATAL=no IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_ ...

  10. 关于thinkphp5被入侵后的一些思考

    最近一段时间thinkphp5爆出漏洞  request.php中的请求过滤不严 是得web端 可以直接写入一个文件到服务器上 进而可得webshell权限 我的一个客户 就是这样被入侵了   刚开始 ...