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. [转]如何将mysql表结构导出成Excel格式的(并带备注)

    方法一: 1.使用一个MySQL管理工具:SQLyog,点击菜单栏“数据库”下拉的最后一项: 导出的格式如下: 2.要想转成Excel格式的只需手动将该表复制到Excel中去. 方法二: 1.以下用的 ...

  2. python全栈开发day72-django之Form组件

    一.ajax 1. 复习JSON 1. JSON是什么? 一种数据格式,和语言无关的数据格式. 2. Python里面转换 1. Python对象 --> 字符串 import json 字符串 ...

  3. 2018牛客网暑假ACM多校训练赛(第四场)B Interval Revisited 动态规划

    原文链接https://www.cnblogs.com/zhouzhendong/p/NowCoder-2018-Summer-Round4-B.html 题目传送门 - https://www.no ...

  4. {}动态规划}记忆化dp

    先搞个模板 #include<stdio.h> #include<string.h> using namespace std; typedef long long ll; ]; ...

  5. hibernate log4j 输出sql

    applicationContext.xml <bean id="sessionFactory"        class="org.springframework ...

  6. shell编程第二天

  7. SpringBoot使用WebFlux响应式编程操作数据库

    这一篇文章介绍SpringBoot使用WebFlux响应式编程操作MongoDb数据库. 前言 在之前一篇简单介绍了WebFlux响应式编程的操作,我们在来看一下下图,可以看到,在目前的Spring ...

  8. XamarinSQLite教程创建数据表

    XamarinSQLite教程创建数据表 新创建的数据库没有任何表.开发者需要手动添加数据表,并添加测试所需的数据. 1.创建数据表 为了存储数据,开发者需要添加自己的表,并设计表的结构.操作步骤如下 ...

  9. RFID的winform程序心得2

    RFID的winform程序心得1 webBrowser1.AllowWebBrowserDrop = false;//将 WebBrowser 控件的 AllowWebBrowserDrop 属性设 ...

  10. malloc函数详解 C语言逻辑运算符

    今天写线性表的实现,又遇到了很多的难题,C语言的指针真的没学扎实.很多基础都忘了. 一是 :malloc 函数的使用. 二是:C语言逻辑运算符. 一.原型:extern void *malloc(un ...