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;}
-->
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;}
-->

作者

彭东林
pengdonglin137@163.com
 

平台

Linux-4.14.13
Qemu + vexpress
 

概述

前面介绍了single_open,下面结合一个简单的demo驱动,学习一下seq_file的用法。
下面是一张示意图:
 

正文

seq_demo驱动里实现了一个简单的链表,在init的时候会依次创建7个节点并加入链表,然后向用户空间导出一个seq_demo的节点,读取这个节点,就会调用seq_file相关函数对链表进行遍历,输出每个节点的相关信息。

一、seq_demo驱动

#include <linux/init.h>
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/slab.h>

static struct dentry *seq_demo_dir;
static LIST_HEAD(seq_demo_list);
static DEFINE_MUTEX(seq_demo_lock);

struct seq_demo_node {
char name[10];
struct list_head list;
};

static void *seq_demo_start(struct seq_file *s, loff_t *pos)
{
mutex_lock(&seq_demo_lock);

return seq_list_start(&seq_demo_list, *pos);
}

static void *seq_demo_next(struct seq_file *s, void *v, loff_t *pos)
{
return seq_list_next(v, &seq_demo_list, pos);
}

static void seq_demo_stop(struct seq_file *s, void *v)
{
mutex_unlock(&seq_demo_lock);
}

static int seq_demo_show(struct seq_file *s, void *v)
{
struct seq_demo_node *node = list_entry(v, struct seq_demo_node, list);

seq_printf(s, "name: %s, addr: 0x%p\n", node->name, node);

return 0;
}

static const struct seq_operations seq_demo_ops = {
.start = seq_demo_start,
.next = seq_demo_next,
.stop = seq_demo_stop,
.show = seq_demo_show,
};

static int seq_demo_open(struct inode *inode, struct file *file)
{
return seq_open(file, &seq_demo_ops);
}

static const struct file_operations seq_demo_fops = {
.owner = THIS_MODULE,
.open = seq_demo_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};

static int __init seq_demo_init(void)
{
int i;
struct seq_demo_node *node;

for (i = 0; i < 7; i++) {
node = kzalloc(sizeof(struct seq_demo_node), GFP_KERNEL);
sprintf(node->name, "node%d", i);

INIT_LIST_HEAD(&node->list);
list_add_tail(&node->list, &seq_demo_list);
}

seq_demo_dir = debugfs_create_file("seq_demo", 0444, NULL,
NULL, &seq_demo_fops);
return 0;
}

static void __exit seq_demo_exit(void)
{
struct seq_demo_node *node_pos, *node_n;

if (seq_demo_dir) {
debugfs_remove(seq_demo_dir);
list_for_each_entry_safe(node_pos, node_n, &seq_demo_list, list)
if (node_pos) {
printk("%s: release %s\n", __func__, node_pos->name);
kfree(node_pos);
}
}
}

module_init(seq_demo_init);
module_exit(seq_demo_exit);
MODULE_LICENSE("GPL");

 #include <linux/init.h>
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/slab.h> static struct dentry *seq_demo_dir;
static LIST_HEAD(seq_demo_list);
static DEFINE_MUTEX(seq_demo_lock); struct seq_demo_node {
char name[];
struct list_head list;
}; static void *seq_demo_start(struct seq_file *s, loff_t *pos)
{
mutex_lock(&seq_demo_lock); return seq_list_start(&seq_demo_list, *pos);
} static void *seq_demo_next(struct seq_file *s, void *v, loff_t *pos)
{
return seq_list_next(v, &seq_demo_list, pos);
} static void seq_demo_stop(struct seq_file *s, void *v)
{
mutex_unlock(&seq_demo_lock);
} static int seq_demo_show(struct seq_file *s, void *v)
{
struct seq_demo_node *node = list_entry(v, struct seq_demo_node, list); seq_printf(s, "name: %s, addr: 0x%p\n", node->name, node); return ;
} static const struct seq_operations seq_demo_ops = {
.start = seq_demo_start,
.next = seq_demo_next,
.stop = seq_demo_stop,
.show = seq_demo_show,
}; static int seq_demo_open(struct inode *inode, struct file *file)
{
return seq_open(file, &seq_demo_ops);
} static const struct file_operations seq_demo_fops = {
.owner = THIS_MODULE,
.open = seq_demo_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
}; static int __init seq_demo_init(void)
{
int i;
struct seq_demo_node *node; for (i = ; i < ; i++) {
node = kzalloc(sizeof(struct seq_demo_node), GFP_KERNEL);
sprintf(node->name, "node%d", i); INIT_LIST_HEAD(&node->list);
list_add_tail(&node->list, &seq_demo_list);
} seq_demo_dir = debugfs_create_file("seq_demo", , NULL,
NULL, &seq_demo_fops);
return ;
} static void __exit seq_demo_exit(void)
{
struct seq_demo_node *node_pos, *node_n; if (seq_demo_dir) {
debugfs_remove(seq_demo_dir);
list_for_each_entry_safe(node_pos, node_n, &seq_demo_list, list)
if (node_pos) {
printk("%s: release %s\n", __func__, node_pos->name);
kfree(node_pos);
}
}
} module_init(seq_demo_init);
module_exit(seq_demo_exit);
MODULE_LICENSE("GPL");

下面是运行结果:

[root@vexpress mnt]# cat /d/seq_demo
name: node0, addr: 0xef252000
name: node1, addr: 0xef252680
name: node2, addr: 0xef252380
name: node3, addr: 0xef252740
name: node4, addr: 0xef252b00
name: node5, addr: 0xee80e480
name: node6, addr: 0xeeb9fd40
name: node7, addr: 0xeeb9fd00

二、分析

在遍历链表的时候使用seq_file提供的通用接口函数,当然也可以自己实现,只需要遵循如下原则:
start:根据索引编号pos找到对应的node,并返回该node的地址,也就是show和next方法里的v
next:根据当前node的地址和索引编号计算下一个node的地址和索引编号pos,返回值就是下一个节点的地址
show:输出传入的node的信息
stop:如果在start里有加锁,那么在这里需要释放锁
 
结合上面的驱动分析一下:
seq_list_start:
 struct list_head *seq_list_start(struct list_head *head, loff_t pos)
{
struct list_head *lh; list_for_each(lh, head)
if (pos-- == )
return lh; return NULL;
}
遍历链表,寻找索引为pos的项,找到的话,返回地址,否则返回NULL。当返回NULL的时候, stop会被调用。
 
seq_list_next:
 struct list_head *seq_list_next(void *v, struct list_head *head, loff_t *ppos)
{
struct list_head *lh; lh = ((struct list_head *)v)->next;
++*ppos;
return lh == head ? NULL : lh;
}
计算下一项的地址和索引,如果遍历结束,即lh==head,返回NULL,否则返回下一项的地址。当返回NULL的时候,stop会被调用并把缓冲区中的内容吐给用户
 
 
未完待续

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

  1. seq_file学习(1)—— single_open

    span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }.CodeMirror ...

  2. Seq_file文件系统实例剖析

    http://blog.chinaunix.net/uid-24432676-id-2607766.html 另 http://www.cnblogs.com/qq78292959/archive/2 ...

  3. linux内核seq_file接口

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

  4. 序列文件(seq_file)接口

    转载:http://blog.csdn.net/gangyanliang/article/details/7244664 内容简介: 本文主要讲述序列文件(seq_file)接口的内核实现,如何使用它 ...

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

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

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

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

  7. Non Lasting Storage File System、procfs、sysfs

    catalog . 引言 . proc文件系统 . 简单的文件系统 . sysfs 0. 引言 传统上,文件系统用于在块设备上持久存储数据,但也可以使用文件系统来组织.提供.交换并不存储在块设备上的信 ...

  8. The Kernel Newbie Corner: Kernel Debugging with proc "Sequence" Files--Part 3

    转载:https://www.linux.com/learn/linux-career-center/44184-the-kernel-newbie-corner-kernel-debugging-w ...

  9. imx51-linux的cpuinfo之分析

    这两天客户提出来,我们的平板cat /proc/cpuinfo出来的信息中的serial怎么是0. 客户就是上帝啊,没办法,分析找问题贝. 我们先看一下目前的cat /proc/cpuinfo的信息: ...

随机推荐

  1. bash: cannot create temp file for here-document: Read-only file system

    文件系统被强制只读问题,第一眼看到百度了一下,说可能磁盘坏了.卧槽我都吓懵了系统盘坏了,闹着玩呢,然后接着查资料,排查 mount 查看所有挂载,发现根目录的挂载权限是ro只读. /dev/sda2 ...

  2. vue-cli 部分浏览器不支持es6的语法-babel-polyfill的引用和使用

    npm install --save-dev babel-polyfill babel-polyfill用正确的姿势安装之后,引用方式有三种: 1.require("babel-polyfi ...

  3. 函数对象的call()、apply() 方法区别

    函数对象的call().apply() 方法 函数作为对象提供了call(),apply() 方法,他们也可以用来调用函数,这两个方法都接受一个对象作为参数,用来指定本次调用时函数中this的指向: ...

  4. 000 Security的计划

    可重用的,企业级的,认证和授权模块 1.主要涉及的模块 2.最终的目标 3.开发的项目模块结构

  5. mysql group by组内排序

    mysql group by组内排序:     首先是组外排序:     SELECT z.create_time,z.invoice_id from qf_invoice_log z where z ...

  6. vdom,diff,key 算法的了解

    <ul id='list'> <li class='item'>Item1</li> <li class='item'>Item2 </li> ...

  7. JAVA首次课堂测试总结

    暑期生活已经结束,新的学期也已经开始,而暑期放假之前约定的JAVA首次课堂测试也如期的到来,本次测试真的可以学到和多东西,也有很多感想. 首先体会最深的就是系主任所说的软件工程不是那么好学的,真的需要 ...

  8. pacman 命令

    安装 pacman -S  删除 pacman -R  移除已安装不需要软件包 pacman -Rs  删除一个包,所有依赖 pacman -Rsc  升级包 pacman -Syu  查询包数据库 ...

  9. JavaScript函数、闭包、原型、面向对象

    JavaScript函数.闭包.原型.面向对象 断言 单元测试框架的核心是断言方法,通常叫assert(). 该方法通常接收一个值--需要断言的值,以及一个表示该断言目的的描述. 如果该值执行的结果为 ...

  10. C# SQLiteHelper

    using System; using System.Data; using System.Data.Common; using System.Data.SQLite; using System.IO ...