zoukankan      html  css  js  c++  java
  • seq_file学习(1)—— single_open

    作者

    彭东林
    pengdonglin137@163.com
     

    平台

    Linux-4.14.13
    Qemu + vexpress
     

    概述

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

    正文

    示例程序
     1 #include <linux/init.h>
     2 #include <linux/module.h>
     3 #include <linux/seq_file.h>
     4 #include <linux/debugfs.h>
     5 #include <linux/fs.h>
     6 
     7 static struct dentry *seq_file_demo_dir;
     8 
     9 static int seq_file_demo_show(struct seq_file *seq, void *v)
    10 {
    11         seq_printf(seq, "Hello World
    ");
    12         return 0;
    13 }
    14 
    15 static int seq_file_demo_open(struct inode *inode, struct file *file)
    16 {
    17         return single_open(file, &seq_file_demo_show, NULL);
    18 }
    19 
    20 static const struct file_operations seq_file_demo_fops = {
    21         .owner = THIS_MODULE,
    22         .open = seq_file_demo_open,
    23         .read = seq_read,
    24         .llseek = seq_lseek,
    25         .release = single_release,
    26 };
    27 
    28 static int __init seq_file_demo_init(void)
    29 {
    30         seq_file_demo_dir = debugfs_create_file("seq_file_demo", 0444, NULL,
    31                 NULL, &seq_file_demo_fops);
    32         return 0;
    33 }
    34 
    35 static void __exit seq_file_demo_exit(void)
    36 {
    37         if (seq_file_demo_dir)
    38                 debugfs_remove(seq_file_demo_dir);
    39 }
    40 
    41 module_init(seq_file_demo_init);
    42 module_exit(seq_file_demo_exit);
    43 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中提供了下面的宏定义:
     1 #define DEFINE_SHOW_ATTRIBUTE(__name)                    
     2 static int __name ## _open(struct inode *inode, struct file *file)    
     3 {                                    
     4     return single_open(file, __name ## _show, inode->i_private);    
     5 }                                    
     6                                     
     7 static const struct file_operations __name ## _fops = {            
     8     .owner        = THIS_MODULE,                    
     9     .open        = __name ## _open,                
    10     .read        = seq_read,                    
    11     .llseek        = seq_lseek,                    
    12     .release    = single_release,                
    13 }

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

     1 #include <linux/init.h>
     2 #include <linux/module.h>
     3 #include <linux/seq_file.h>
     4 #include <linux/debugfs.h>
     5 #include <linux/fs.h>
     6 
     7 static struct dentry *seq_file_demo_dir;
     8 
     9 static int seq_file_demo_show(struct seq_file *seq, void *v)
    10 {
    11         seq_printf(seq, "Hello World
    ");
    12         return 0;
    13 }
    14 DEFINE_SHOW_ATTRIBUTE(seq_file_demo);
    15 
    16 static int __init seq_file_demo_init(void)
    17 {
    18         seq_file_demo_dir = debugfs_create_file("seq_file_demo", 0444, NULL,
    19                 NULL, &seq_file_demo_fops);
    20         return 0;
    21 }
    22 
    23 static void __exit seq_file_demo_exit(void)
    24 {
    25         if (seq_file_demo_dir)
    26                 debugfs_remove(seq_file_demo_dir);
    27 }
    28 
    29 module_init(seq_file_demo_init);
    30 module_exit(seq_file_demo_exit);
    31 MODULE_LICENSE("GPL");
    这样我们只需要专心实现show函数即可。
    如果看一下single_open,会发现它是对seq_file的进一步封装:
     1 int single_open(struct file *file, int (*show)(struct seq_file *, void *),
     2         void *data)
     3 {
     4     struct seq_operations *op = kmalloc(sizeof(*op), GFP_KERNEL);
     5     int res = -ENOMEM;
     6 
     7     if (op) {
     8         op->start = single_start;
     9         op->next = single_next;
    10         op->stop = single_stop;
    11         op->show = show;
    12         res = seq_open(file, op);
    13         if (!res)
    14             ((struct seq_file *)file->private_data)->private = data;
    15         else
    16             kfree(op);
    17     }
    18     return res;
    19 }

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

     1 static void *single_start(struct seq_file *p, loff_t *pos)
     2 {
     3     return NULL + (*pos == 0);
     4 }
     5 
     6 static void *single_next(struct seq_file *p, void *v, loff_t *pos)
     7 {
     8     ++*pos;
     9     return NULL;
    10 }
    11 
    12 static void single_stop(struct seq_file *p, void *v)
    13 {
    14 }

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

    single_start的返回值表示要输出的元素的首地址,这个函数的作用是找到索引号为*pos的元素,并返回该元素的首地址,此外也可以做一些加锁的操作
    single_next的入参中v表示刚刚show过的元素的首地址,*pos是该元素的索引,这个函数的目的是计算并返回下一个要show的元素的首地址以及索引号
    single_stop里可以做一些释放锁的操作
    show需要自己实现,向用户show出当前元素的相关信息
     
     
    未完待续
  • 相关阅读:
    地址栏传值 JS取值方法
    定位导航 制作
    验证码
    图片水印
    AJAX 三级联动
    javascript 和Jquery 互转
    Jquery 事件 DOM操作
    Jquery 基础
    软件工程中的形式化方法读后感
    软件工程理论、方法与实践 需求工程读后感
  • 原文地址:https://www.cnblogs.com/pengdonglin137/p/8439777.html
Copyright © 2011-2022 走看看