zoukankan      html  css  js  c++  java
  • 设备驱动基础学习--/proc下增加节点

    在需要创建一个由一系列数据顺序组合而成的/proc虚拟文件或一个较大的/proc虚拟文件时,推荐使用seq_file接口。

    数据结构struct seq_fille定义在include/linux/seq_file.h

    struct seq_file {

    char *buf;     //seq_file接口使用的缓存页指针

    size_t size;   //seq_file接口使用的缓存页大小

    size_t from;   //从seq_file中向用户态缓冲区拷贝时相对于buf的偏移地址

    size_t count;  //buf中可以拷贝到用户态的字符数目 

    loff_t index;  //start、next的处理的下标pos数值

    loff_t read_pos;  //当前已拷贝到用户态的数据量大小

    u64 version;

    struct mutex lock;  //针对此seq_file操作的互斥锁,所有seq_*的访问都会上锁

    const struct seq_operations *op; //操作实际底层数据的函数

    void *private;

    };

    这里我们大致介绍下struct seq_operations中的各个函数的作用:

      void * (*start) (struct seq_file *m, loff_t *pos);

    start 方法会首先被调用,它的作用是在设置访问的起始点。

    m:指向的是本seq_file的结构体,在正常情况下无需处理。

    pos:是一个整型位置值,指示开始读取的位置。对于这个位置的意义完全取决于底层实现,不一定是字节为单位的位置,可能是一个元素的序列号。返回值如果非NULL,则是一个指向迭代器实现的私有数据结构体指针。如果访问出错则返回NULL。

     设置好了访问起始点,seq_file内部机制可能会使用show方法获取start返回值指向的结构体中的数据到内部缓存,并适时送往用户空间。

     int (*show) (struct seq_file *m, void *v);

     所以show方法就是负责将v指向元素中的数据输出到seq_file的内部缓存,但是其中必须借助seq_file提供的一些类似printf的接口函数:

    int seq_printf(struct seq_file *sfile, const char *fmt, ...);//专为 seq_file 实现的类似 printf 的函数;用于将数据常用的格式串和附加值参数.

    //你必须将给 show 函数的 set_file 结构指针传递给它。如果seq_printf 返回-1,意味着缓存区已满,部分输出被丢弃。但是大部分时候都忽略了其返回值。

    int seq_putc(struct seq_file *sfile, char c);

    int seq_puts(struct seq_file *sfile, const char *s);//类似 putc 和 puts 函数的功能,sfile参数和返回值与 seq_printf相同。

    int seq_escape(struct seq_file *m, const char *s, const char *esc);//这个函数类似 seq_puts ,但是它会将 s 中所有在 esc 中出现的字符以八进制格式输出到缓存。

    //esc 的常用值是" \", 它使内嵌的空格不会搞乱输出或迷惑 shell 脚本.

     
      int seq_write(struct seq_file *seq, const void *data, size_t len) 
      直接将data指向的数据写入seq_file缓存,数据长度为len。用于非字符串数据。
     int seq_path(struct seq_file *sfile, struct vfsmount *m, struct dentry *dentry, char *esc);//这个函数能够用来输出给定目录项关联的文件名,驱动极少使用。  

     在show函数返回之后,seq_file机制可能需要移动到下一个数据元素,那就必须使用next方法。

    void * (*next) (struct seq_file *m, void *v, loff_t *pos);

    v:是之前调用start或next返回的元素指针,可能是上一个show已经完成输出所指向的元素。

    pos:需要移动到的元素索引值。


      在next实现中应当递增pos指向的值,但是具体递增的数量和迭代器的实现有关,不一定是1。而next的返回值如果非NULL,则是下一个需要输出到缓存的元素指针,否则表明已经输出结束,将会调用stop方法做清理。

     void (*stop) (struct seq_file *m, void *v);

     在stop实现中,参数m指向本seq_file的结构体,在正常情况下无需处理。而v是指向上一个next或start返回的元素指针。在需要做退出处理的时候才需要实现具体的功能。但是许多情况下可以直接返回。

    在next和start的实现中可能需要对一个序列的函数进行遍历,而在内核中,对于一个序列数据结构体的实现一般是使用双向链表或者哈希链表,所有seq_file同时提供了一些对于内核双向链表和哈希链表的封装接口函数,方便程序员实现对于通过链表链接的结构体序列的操作。这些函数名一般是seq_list_*或者seq_hlist_*,这些函数的实现都在fs/seq_file.c中。

    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/types.h>
    #include <linux/fs.h>
    #include <linux/errno.h>
    #include <linux/mm.h>
    #include <linux/slab.h>
    #include <asm/io.h>
    #include <asm/uaccess.h>
    #include <linux/proc_fs.h>
    #include <linux/seq_file.h>

    static struct proc_dir_entry *proc_root;
    static struct proc_dir_entry *proc_entry;
    #define USER_ROOT_DIR "fellow_root"
    #define USER_ENTRY "fellow_entry"
    #define INFO_LEN 16
    char *info;
    static int proc_fellow_show(struct seq_file *m, void *v)
    {

      seq_printf(m, "%s ",info);
      return 0;
    }

    static int proc_fellow_open(struct inode *inode, struct file *file)
    {
      return single_open(file, proc_fellow_show, NULL);//而调用single_open函数只需直接指定show的函数指针即可,个人猜测可能是在single_open函数中实现了seq_operations结构体 。如果使用seq_open则需要实现seq_operations。

    }

    static ssize_t proc_fellow_write(struct file *file, const char __user *buffer,  size_t count, loff_t *f_pos)
    {
       if ( count > INFO_LEN)
      return -EFAULT;
      if(copy_from_user(info, buffer, count))
      {
        return -EFAULT;
      }
      return count;
    }
    static const struct file_operations fellow_proc_fops = {
    .owner = THIS_MODULE,
    .open = proc_fellow_open,
    .read = seq_read,
    .write = proc_fellow_write,
    .llseek = seq_lseek,
    .release = single_release,
    };

    static int fellow_create_proc_entry(void)
    {
      int error = 0;
      proc_root = proc_mkdir(USER_ROOT_DIR, NULL); //创建目录
      if (NULL==proc_root)
      {
        printk(KERN_ALERT"Create dir /proc/%s error! ", USER_ROOT_DIR);
        return -ENOMEM;
      }
      printk(KERN_INFO"Create dir /proc/%s ", USER_ROOT_DIR);

      // proc_entry =create_proc_entry(USER_ENTRY, 0666, proc_root);
      proc_entry = proc_create(USER_ENTRY, 0666, proc_root, &fellow_proc_fops); //在proc_root下创建proc_entry.
      if (NULL ==proc_entry)
      {
        printk(KERN_ALERT"Create entry %s under /proc/%s error! ", USER_ENTRY,USER_ROOT_DIR);
        error = -ENOMEM;
        goto err_out;

      }

    //proc_entry->write_proc= fellow_writeproc;
    //proc_entry->read_proc = fellow_readproc;
      printk(KERN_INFO"Create /proc/%s/%s ", USER_ROOT_DIR,USER_ENTRY);

      return 0;
    err_out:
    //proc_entry->read_proc = NULL;
    //proc_entry->write_proc= NULL;
      remove_proc_entry(USER_ENTRY, proc_root);
      remove_proc_entry(USER_ROOT_DIR, NULL);
      return error;
    }
    static int fellowproc_init(void)
    {
      int ret = 0;
      printk("fellowproc_init ");

      info = kmalloc(INFO_LEN * sizeof(char), GFP_KERNEL);
      if (!info)
      {
        ret = -ENOMEM;
        goto fail;
      }
      memset(info, 0, INFO_LEN);
      fellow_create_proc_entry();
      return 0;
    fail:
      return ret;
    }

    static void fellowproc_exit(void)
    {
      kfree(info);

      remove_proc_entry(USER_ENTRY, proc_root);
      remove_proc_entry(USER_ROOT_DIR, NULL);
    }

    MODULE_AUTHOR("fellow");
    MODULE_LICENSE("GPL");
    module_init(fellowproc_init);
    module_exit(fellowproc_exit);

    运行结果如下:

  • 相关阅读:
    这样的面试题你会吗
    5个强盗分100颗宝石 你该怎么分?
    《Java程序员全攻略:从小工到专家》连载十四:先挣的是资本,后挣的是钱
    不是三维——软件项目的设计、开发与管理
    《Java程序员职场全攻略:从小工到专家》连载十三:给自己估个好价
    程序开发心理学
    电子工业出版社博文视点图书在微软VS2010全球发布会上受追捧
    博文视点大讲堂25期——2天玩转单反相机
    《Java程序员职场全攻略:从小工到专家》连载十五:我们不是爱加班
    管理小品(1)
  • 原文地址:https://www.cnblogs.com/fellow1988/p/6263054.html
Copyright © 2011-2022 走看看