span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }.cm-searching {background: #ffa; background: rgba(255, 255, 0, .4);}.cm-force-border { padding-right: .1px; }@media print { .CodeMirror div.CodeMirror-cursors {visibility: hidden;}}.cm-tab-wrap-hack:after { content: ""; }span.CodeMirror-selectedtext { background: none; }.CodeMirror-activeline-background, .CodeMirror-selected {transition: visibility 0ms 100ms;}.CodeMirror-blur .CodeMirror-activeline-background, .CodeMirror-blur .CodeMirror-selected {visibility:hidden;}.CodeMirror-blur .CodeMirror-matchingbracket {color:inherit !important;outline:none !important;text-decoration:none !important;}
-->
li {list-style-type:decimal;}ol.wiz-list-level2 > li {list-style-type:lower-latin;}ol.wiz-list-level3 > li {list-style-type:lower-roman;}blockquote {padding:0 12px;padding:0 0.75rem;}blockquote > :first-child {margin-top:0;}blockquote > :last-child {margin-bottom:0;}img {border:0;max-width:100%;height:auto !important;margin:2px 0;}table {border-collapse:collapse;border:1px solid #bbbbbb;}td, th {padding:4px 8px;border-collapse:collapse;border:1px solid #bbbbbb;min-height:28px;word-break:break-all;box-sizing: border-box;}.wiz-hide {display:none !important;}
-->

作者

彭东林
pengdonglin137@163.com
 

平台

Linux-4.14.13
Qemu + vexpress
 

概述

从内核中导出信息到用户空间有很多方法,可以自己去实现file_operations的read函数或者mmap函数,但是这种方法不够简单,而且也会有一些限制,比如一次read读取大于1页时,驱动里就不得不去进行复杂的缓冲区管理。为此,就需要学习一下seq_file的用法,为了更简单和方便,内核提供了single_xxx系列接口,它是对seq_file的进一步封装。
 

正文

示例程序
 #include <linux/init.h>
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <linux/fs.h> static struct dentry *seq_file_demo_dir; static int seq_file_demo_show(struct seq_file *seq, void *v)
{
seq_printf(seq, "Hello World\n");
return ;
} static int seq_file_demo_open(struct inode *inode, struct file *file)
{
return single_open(file, &seq_file_demo_show, NULL);
} static const struct file_operations seq_file_demo_fops = {
.owner = THIS_MODULE,
.open = seq_file_demo_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
}; static int __init seq_file_demo_init(void)
{
seq_file_demo_dir = debugfs_create_file("seq_file_demo", , NULL,
NULL, &seq_file_demo_fops);
return ;
} static void __exit seq_file_demo_exit(void)
{
if (seq_file_demo_dir)
debugfs_remove(seq_file_demo_dir);
} module_init(seq_file_demo_init);
module_exit(seq_file_demo_exit);
MODULE_LICENSE("GPL");
上面的demo在/sys/kernel/debug/下创建了一个"seq_file_demo",读取这个文件会得到"Hello World"字符串。上面的函数中需要我们实现的只有一个:seq_file_demo_show,直接调用seq_file提供的输出函数即可,不用我们去考虑缓冲区的分配、释放以及越界等问题,可以尽情畅快的输出。
上面的代码里有些标准化的函数,看着有些碍眼,所以在Linux-4.15的include/linux/seq_file.h中提供了下面的宏定义:
 #define DEFINE_SHOW_ATTRIBUTE(__name)                    \
static int __name ## _open(struct inode *inode, struct file *file) \
{ \
return single_open(file, __name ## _show, inode->i_private); \
} \
\
static const struct file_operations __name ## _fops = { \
.owner = THIS_MODULE, \
.open = __name ## _open, \
.read = seq_read, \
.llseek = seq_lseek, \
.release = single_release, \
}

利用上面的宏可以对我们的驱动做进一步简化:

 #include <linux/init.h>
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <linux/fs.h> static struct dentry *seq_file_demo_dir; static int seq_file_demo_show(struct seq_file *seq, void *v)
{
seq_printf(seq, "Hello World\n");
return ;
}
DEFINE_SHOW_ATTRIBUTE(seq_file_demo); static int __init seq_file_demo_init(void)
{
seq_file_demo_dir = debugfs_create_file("seq_file_demo", , NULL,
NULL, &seq_file_demo_fops);
return ;
} static void __exit seq_file_demo_exit(void)
{
if (seq_file_demo_dir)
debugfs_remove(seq_file_demo_dir);
} module_init(seq_file_demo_init);
module_exit(seq_file_demo_exit);
MODULE_LICENSE("GPL");
这样我们只需要专心实现show函数即可。
如果看一下single_open,会发现它是对seq_file的进一步封装:
 int single_open(struct file *file, int (*show)(struct seq_file *, void *),
void *data)
{
struct seq_operations *op = kmalloc(sizeof(*op), GFP_KERNEL);
int res = -ENOMEM; if (op) {
op->start = single_start;
op->next = single_next;
op->stop = single_stop;
op->show = show;
res = seq_open(file, op);
if (!res)
((struct seq_file *)file->private_data)->private = data;
else
kfree(op);
}
return res;
}

上面设置了single_xx的start、next以及stop回调函数,实现很简单:

 static void *single_start(struct seq_file *p, loff_t *pos)
{
return NULL + (*pos == );
} static void *single_next(struct seq_file *p, void *v, loff_t *pos)
{
++*pos;
return NULL;
} static void single_stop(struct seq_file *p, void *v)
{
}

其中,*ops表示的是要输出的元素的索引编号,从0开始,依次递增;

single_start的返回值表示要输出的元素的首地址,这个函数的作用是找到索引号为*pos的元素,并返回该元素的首地址,此外也可以做一些加锁的操作
single_next的入参中v表示刚刚show过的元素的首地址,*pos是该元素的索引,这个函数的目的是计算并返回下一个要show的元素的首地址以及索引号
single_stop里可以做一些释放锁的操作
show需要自己实现,向用户show出当前元素的相关信息
 
 
未完待续

seq_file学习(1)—— single_open的更多相关文章

  1. seq_file学习(2)—— seq_file

    li {list-style-type:decimal;}ol.wiz-list-level2 > li {list-style-type:lower-latin;}ol.wiz-list-le ...

  2. Linux内核学习笔记之seq_file接口创建可读写proc文件

    转自:http://blog.csdn.net/mumufan05/article/details/45803219 学习笔记与个人理解,如有错误,欢迎指正. 温馨提示:建议跟着注释中的编号顺序阅读代 ...

  3. linux内核数据结构学习总结

    目录 . 进程相关数据结构 ) struct task_struct ) struct cred ) struct pid_link ) struct pid ) struct signal_stru ...

  4. Introduction the naive“scull” 《linux设备驱动》 学习笔记

    Introduction the naive "scull" 首先.什么是scull? scull (Simple Character Utility for Loading Lo ...

  5. linux内核seq_file接口

    seq相关头文件linux/seq_file.h,seq相关函数的实现在fs/seq_file.c.seq函数最早是在2001年就引入了,但以前内核中一直用得不多,而到了2.6内核后,许多/proc的 ...

  6. itop4412学习-超级块操作

    1. 先看下超级块支持的函数列表,文件路径\4412_SCP精英版\Android源码\iTop4412_Kernel_3.0_20140521\iTop4412_Kernel_3.0\include ...

  7. LKM rootkit:Reptile学习

    简介 Reptile是github上一个很火的linux lkm rootkit,最近学习了一些linux rootkit的内容,在这里记录一下. 主要是分析reptile的实现 Reptile的使用 ...

  8. 设备驱动基础学习--/proc下增加节点

    在需要创建一个由一系列数据顺序组合而成的/proc虚拟文件或一个较大的/proc虚拟文件时,推荐使用seq_file接口. 数据结构struct seq_fille定义在include/linux/s ...

  9. Linux内核驱动学习(三)字符型设备驱动之初体验

    Linux字符型设备驱动之初体验 文章目录 Linux字符型设备驱动之初体验 前言 框架 字符型设备 程序实现 cdev kobj owner file_operations dev_t 设备注册过程 ...

随机推荐

  1. [转]CR, LF, CR/LF区别与关系

    http://weizhifeng.net/talking-about-cr-lf.html 前言 在文本处理中,CR(Carriage Return),LF(Line Feed),CR/LF是不同操 ...

  2. 实际生产用法CMS和G1

    java -Xms100m -Xmx100m -Xmn50m -XX:MetaspaceSize=20m -XX:MaxMetaspaceSize=20m -XX:+UseConcMarkSweepG ...

  3. eclipse启动web应用

    在建好web项目的基础上: (1)配置tomcat服务器 点击window---->Preference----->Server---->Runtime Environment--- ...

  4. outlook邮件中样式问题

    目前要做一个定时发送邮件的功能,邮件的大致内容布局如下: HTML中 在QQ邮件中,可以进行正常显示. 在outlook网页版,也可以正常显示, outlook客户端 但是到了客户端就会出现很多很神奇 ...

  5. php第二天 开始连接数据库

    php连接数据库有三种方法,分别是mysqli面向对象,mysqli面向过程,pdo. 1.查了资料,最终选择则了mysqli面向过程的方式,运行效率应该要高一些. 代码如下 <?php $se ...

  6. datatable 转list ,list转datatable

    方法一:  public static IList<T> ConvertToModel(DataTable dt)             {                // 定义集合 ...

  7. Django 1.11.7学习,配置MySQL数据库(python3.5)

    1.链接MySQL数据库 a.导入pymysql包 import pymysql pymysql.install_as_MySQLdb() b.改变DATABASES参数 ENGINE : 指定数据库 ...

  8. netty03(基于4.1.23.Final 版本的案例)

    基于前面的netty01和netty02修改一下版本,可以点进去的 将maven依赖版本改了一下 <!-- netty版本:netty-5.0.0.Alpha2 http://files.cnb ...

  9. 1、Qt Project之基本文件打开与保存

    基本文件打开与保存: 首先是涉及到的头文件,我们需要在mainwindow.h包含头文件: #include <QFileDialog> #include <QFile> #i ...

  10. A - Character Encoding HDU - 6397 - 方程整数解-容斥原理

    A - Character Encoding HDU - 6397 思路 : 隔板法就是在n个元素间的(n-1)个空中插入k-1个板,可以把n个元素分成k组的方法 普通隔板法 求方程 x+y+z=10 ...