Linux seq_printf输出内容不完整的问题
Linux seq_printf输出内容不完整的问题
写在前面的话:这是多年前在项目中遇到的问题,作为博客的开篇之作,有不足之处,请各位大侠斧正!谢谢!
seq_file接口介绍
有许多种方法能够实现设备驱动(或其它内核组件)提供信息给用户或系统管理员。一个有用的技术是在debugfs,/proc或其他地方创建虚拟文件。虚拟文件能够提供容易获取的人类可读的输出,而且并不需要任何特殊的工具软件,他们能够减轻脚本作者的工作。
seq_file接口就是其中一个能够为内核模块提供信息给用户或管理员的接口,它通过在/proc目录下创建虚拟文件,提供相关内核模块的用户接口。
struct seq_operations 结构体
struct seq_operations提供了seq_file文件的迭代操作接口。其中:
start用于起始访问时文件的初始化工作,并返回一个链接(迭代)对象,或者SEQ_START_TOKEN(表示所有循环的开始);
stop用于文件访问结束时的清理工作,这里文件访问结束表示所有链接对象遍历完毕;
next用于在遍历中寻找下一个链接对象;
show用于对遍历对象的操作,主要调用seq_printf和seq_puts等函数,打印这个对象节点的信息。
struct seq_operations {
void * (*start) (struct seq_file *m, loff_t *pos);
void (*stop) (struct seq_file *m, void *v);
void * (*next) (struct seq_file *m, void *v, loff_t *pos);
int (*show) (struct seq_file *m, void *v);
};
内核模块struct seq_operations结构体定义
在自研内核模块中,skb_status_seq_ops实现当前skb使用状态的打印,其定义如下:
static const struct seq_operations skb_status_seq_ops = {
.start = xxxdriver_seq_start,
.next = xxxdriver_seq_next,
.stop = xxxdriver_seq_stop,
.show = skb_status_seq_show,
};
xxxdriver_seq_start:开始遍历xxxdriver_dev_list的初始化工作;
xxxdriver_seq_next:遍历xxxdriver_dev_list时返回一个xxxdriver链接对象;
xxxdriver_seq_stop:遍历xxxdriver_dev_list完成时,做清理工作;
skb_status_seq_show:输出遍历对象的一些信息。
在skb_status_seq_show函数中,通过seq_printf函数实现信息的打印,其实现如下:
static int skb_status_seq_show(struct seq_file *seq, void *v)
{
if (v == SEQ_START_TOKEN)
{
seq_printf(seq, "%s", "skb status info start:\n");
skbstatusflag = 1;
}
else
{
skb_status_seq_printf_stats(seq, v);
}
return 0;
}
在调用xxxdriver_seq_start函数后,返回SEQ_START_TOKEN,首先输出一行信息:“skb status info start:”。在遍历xxxdriver_dev_list链接对象过程中,调用skb_status_seq_printf_stats函数,输出实际的skb状态信息。
skb_status_seq_printf_stats函数打印的信息:xxxdriver在每个cpu上使用预分配skb的情况,包括预分配skb总数,空闲skb数目,已占用skb数目,skb为空的数目等等。
故障说明
为了调试xxxdriver驱动,新增了skb_status_seq_show输出内容,不幸的是,该功能不能显示全部信息了,只显示了“skb status info start:”信息。
查看seq_printf函数的实现:
int seq_printf(struct seq_file *m, const char *f, ...)
{
va_list args;
int len;
if (m->count < m->size) {
va_start(args, f);
len = vsnprintf(m->buf + m->count, m->size - m->count, f, args);
va_end(args);
if (m->count + len < m->size) {
m->count += len;
return 0;
}
}
m->count = m->size;
return -1;
}
发现seq_printf函数能否继续填充数据,需要判断当前seq_file缓冲区是否还有空间可供使用。于是在xxxdriver skb_status_seq_printf_stats函数中打印seq->count和seq->size的值,发现size大小为4096,而count值在填充完所有信息之前,已经到达4096字节。想当然认为是由于seq_file缓冲区限制导致不能输出全部信息。
seq_file文件输出流程:
在xxxdriver中,seq_flle文件指定的文件操作接口如下:
static const struct file_operations skb_status_seq_fops = {
.owner = THIS_MODULE,
.open = skb_status_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
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);
/* Don't assume *ppos is where we left it */
if (unlikely(*ppos != m->read_pos)) {
m->read_pos = *ppos;
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;
}
}
/*
* 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;
/* grab buffer if we didn't have one */
字节 */
if (!m->buf) {
m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
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;
/* 对应于xxxdriver_seq_start函数,开始扫描驱动链接的对象 */
p = m->op->start(m, &pos);
while (1) {
err = PTR_ERR(p);
if (!p || IS_ERR(p))
break;
err = m->op->show(m, p); /*对应于xxxdriver_seq_show函数*/
/* xxxdriver 会存放字符串:“skb status info start:” */
if (err < 0)
break;
if (unlikely(err))
m->count = 0;
/* 如果没有输出,则查找下一个链接对象 */
if (unlikely(!m->count)) {
/*对应于xxxdriver_seq_next函数*/
p = m->op->next(m, p, &pos);
m->index = pos;
continue;
}
/* 如果输出内容小于当前buf大小,则走填充数据流程 */
字节 */
if (m->count < m->size)
goto Fill;
/* 如果我们一次性输出内容大于当前seq_file->size,还有机会扩大缓冲区大小,重新输出 */
m->op->stop(m, p); /*对应于xxxdriver_seq_stop函数 */
kfree(m->buf);
/* 如果输出内容大于当前buf大小,则扩展当前buf大小 */
m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
if (!m->buf)
goto Enomem;
m->count = 0;
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;
}
/* 在m->size为4096字节情况下,显示相关信息
* 由于函数skb_status_seq_printf_stats里输出其余数据大于4096字节,* 则数据被截断。offs的值为输出第一行“skb status info start:”的长度 */
err = m->op->show(m, p); /* 显示相关数据 */
if (m->count == m->size || err) { /* 已经填满缓冲区或出错 */
m->count = offs;
if (likely(err <= 0))
break;
}
pos = next;
}
m->op->stop(m, p);
/* 对于我们的问题,只copy第一行输出 */
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;
}
PS:您的支持是对博主最大的鼓励
Linux seq_printf输出内容不完整的问题的更多相关文章
- Linux命令 ls -l 输出内容含义详解
Linux命令 ls -l s输出内容含义详解 1. ls 只显示文件名或者文件目录 2. ls -l(这个参数是字母L的小写,不是数字1) 用来查看详细的文件资料 在某个目录下键入ls -l可 ...
- Linux鲜为人知的安全漏洞:不要将输出内容管道给你的shell
将wget或curl输出的内容管道给bash或者sh是一件非常愚蠢的事,例如像下面这样: wget -O - http://example.com/install.sh | sudo sh 命令解释: ...
- Linux命令执行的屏幕输出内容重定向到日志文件
摘要: 作者:Syn良子 出处:http://www.cnblogs.com/cssdongl 转载请注明出处 快速mark一下这个命令细节,免得以后使用又忘记了 大家都知道可以用echo来输出内容到 ...
- HAproxy增加日志记录功能和自定义日志输出内容、格式
http://blog.51cto.com/eric1/1854574 一.增加haproxy日志记录功能 1.1 由于数据分析的需要,我们必须打开haproxy日志,记录相关信息. 在配置前,我 ...
- linux下sort命令使用详解---linux将文本文件内容加以排序命令
转载自:http://www.cnblogs.com/hitwtx/archive/2011/12/03/2274592.html linux下sort命令使用详解---linux将文本文件内容加以排 ...
- Linux 重定向输出到多个文件中
转自:http://codingstandards.iteye.com/blog/833695 用途说明 在执行Linux命令时,我们可以把输出重定向到文件中,比如 ls >a.txt,这时我们 ...
- linux基础教程---内容操作
一.寻找文件里的指定内容 寻找文件里的指定内容,输出内容所在行的所有信息 grep 被搜索内容 文件路径名 >grep var passwd //在 ...
- telnet如何保存输出内容到本地
telnet如何保存输出内容到本地 http://bbs.csdn.net/topics/391023327 一种将程序的标准输出重定向到telnet终端的方法 http://blog.chinaun ...
- 【转】JAVA输出内容打印到TXT以及不同系统中如何换行
JAVA输出内容打印到TXT以及不同系统中如何换行 http://xiyang.09.blog.163.com/blog/static/59827615201172552755293/ 2011-08 ...
随机推荐
- CentOS7 搭建 SVN 服务器
CentOS7 搭建 SVN 服务器 介绍SVN: SVN是Subversion的简称,是一个开放源代码的版本控制系统,相较于RCS.CVS,它采用了分支管理系统,它的设计目标就是取代CVS.互联网上 ...
- Plickers——教师拿手机、学生拿卡片,就可以完成即时全员互动!
全员互动.立刻反馈.无设备添加.无能耗增加,风靡全球教育界,杭州师范大学硕士生导师杨俊锋教授推荐!老师拿手机,学生拿卡片就可以完成! 操作方法: 1.注册:登录www.plicke ...
- python 函数内使用自己的函数名
def p(): import sys print sys._getframe(1).f_code.co_name def f(): p() def f1(): p() if __name__ == ...
- ssl握手数据结构
ssl握手 SSL记录头(5字节) 字节0:记录内容的类型 Content Type Hex Code Description Change_Cipher_Spec 0x14 指示加密方式的更改 Al ...
- python学习笔记八(集合)
集合 set是一个无序的不重复元素序列. 用大括号或set()函数创建集合.注意:创建一个空集合用set(),不用{},{}用来创建空字典 basket = {'apple','orange','ap ...
- PyCharm激活(License server)
打开激活窗口 选择 Activate new license with: License server (用license server 激活) 在 License sever address 处填入 ...
- docker18.09.5 安装与启动、容器、镜像
docker安装与启动 yum -y update 1.卸载老版本的 docker 及其相关依赖yum remove -y docker docker-common container-selinux ...
- go语言学习--string、int、int64互相转换,字符串的截取,数组和字符串的转换
下面总结了go中常用的转换 #string到int int,err:=strconv.Atoi(string) #string到int64 int64, err := strconv.ParseInt ...
- Java核心-多线程-并发控制器-Exchanger交换器
1.基本概念 Exchanger,从名字上理解就是交换.Exchanger用于在两个线程之间进行数据交换,注意也只能在两个线程之间进行数据交换. 线程会阻塞在Exchanger的exchange方法上 ...
- ZooKeeper是按照CP原则构建的,不适合做Service服务发现
一.cap 分布式领域中存在CAP理论,且该理论已被证明:任何分布式系统只可同时满足两点,无法三者兼顾. ①C:Consistency,一致性,数据一致更新,所有数据变动都是同步的. ②A:Avail ...