zoukankan      html  css  js  c++  java
  • 2019.05.08 《Linux驱动开发入门与实战》

    第六章:字符设备

    申请设备号---注册设备

    1、字符设备的框架:

    2、结构体,struct cdev:

    3、字符设备的组成:

    4、例子:

    5、申请和释放设备号:

    设备号和设备节点是什么关系。?

    设备驱动中,很多功能是通过设备号完成的。

    步骤:

    构建字符设备前,应该申请设备号:所用到的函数是下面两个:

    该函数在<fs/char_dev.c>中定义

      int register_chrdev_region(dev_t from, unsigned count, const char *name);

      int alloc_chrdev_region(dev_t *dev, unsigned baseminor,unsigned count, const char *name) ;

    释放设备号,统一使用下面的函数,通常在模块的卸载函数中会调用这个。

      void unregister_chrdev_region(dev_t from, unsigned count);

    申请字符设备的设备号后,需要将字符设备注册到系统中。这样才能使用字符设备。

    为了实现这个过程,需要先解释cdev结构体。cdev结构体描述字符设备,该结构体是

    所有字符设备的抽象,其包含了大量字符设备所共有的特性。

      cdev中kobj成员,用于内核管理字符设备,驱动开发基本不用。

      cdev中ops指向的file_operations结构的指针,该结构定义了操作字符设备的函数。

      cdev中dev是用来存储字符设备所申请的设备号。

      cdev中count表示目前有多少个字符设备在使用改启动程序。

      cdev中list是一个双向链表,用于将其他结构体链接成一个双向链表。这种结构在linux中常用,需要掌握。

    每一个字符设备在/dev目录下都有一个设备文件,打开设备文件就相当于打开相应的字
    符设备。例如应用程序打开设备文件A,那么系统会产生一个inode结点。这样可以通过
    inode结点的i_cdev字段找到cdev字符结构体。通过cdev的ops指针,就能找到设备A的
    操作函数。对操作函数的讲解,将放在后面的内容中。

    file_operations是一个对设备进行操作的抽象结构体。Linux内核的设计非常巧妙。内
    核允许为设备建立一个设备文件,对设备文件的所有操作,就相当于对设备的操作。这
    样的好处是,用户程序可以使用访问普通文件的方法访问设备文件,进而访问设备。这
    样的方法,极大地减轻了程序员的编程负担,程序员不必去熟悉新的驱动接口,就能够
    访问设备。

    对普通文件的访问常常使用open()、read()、write()、close()、ioctl()等方法。同
    样对设备文件的访问,也可以使用这些方法。这些调用最终会引起对
    file_operations结构体中对应函数的调用。对于程序员来说,只要为不同的设备编写
    不同的操作函数就可以了。

    为了使file_operations结构体具有通用性,file_operations会不断地扩充,现在已经非常

    庞大,只需要对file_operations结构体中几个重要的成员进行分析:

      owner成员根本不是一个函数;它是一个指向拥有这个结构模块的指针。这个成员用来
      维持模块的引用计数,当模块还在使用时,不能用rmmod卸载模块。几乎所有时刻,它
      被简单初始化为 THIS_MODULE,一个在<linux/module.h>中定义的宏。

      llseek()函数用来改变文件中的当前读/写位置,并将新位置返回。loff_t参数是一个
      "long long"类型,"long long"类型即使在32位机上也是64位宽。这是为了与64位机兼
      容而定的,因为64位机的文件大小完全可以突破4G。

      read()函数用来从设备中获取数据,成功时函数返回读取的字节数,失败时返回一个负
      的错误码。
      write()函数用来写数据到设备中。成功时该函数返回写入的字节数,失败时返回一个
      负的错误码。
      ioctl()函数提供了一种执行设备特定命令的方法。例如使设备复位,这既不是读操作
      也不是写操作,不适合用read()和write()方法来实现。如果在应用程序中给ioctl传入
      没有定义的命令,那么将返回-ENOTTY的错误,表示该设备不支持这个命令。
      open()函数用来打开一个设备,在该函数中可以对设备进行初始化。如果这个函数被复
      制NULL,那么设备打开永远成功,并不会对设备产生影响。
      release()函数用来释放open()函数中申请的资源,将在文件引用计数为0时,被系统调
      用。其对应应用程序的close()方法,但并不是每一次调用close()方法,都会触发
      release()函数,在对设备文件的所有打开都释放后,才会被调用。

    一般来说,驱动开发人员会将特定设备的特定数据放到cdev结构体后,组成一个新的结
    构体。如图6.3所示,"自定义字符设备"中就包含特定设备的数据。该"自定义设备"中
    有一个cdev结构体。cdev结构体中有一个指向file_operations的指针。这里
    ,file_operations中的函数就可以用来操作硬件,或者"自定义字符设备"中的其他数
    据,从而起到控制设备的作用。

    inode结构体 【这个可以理解为设备节点?】
    内核使用inode结构在系统内部表示文件。inode一般作为file_operations结构中函数的参
    数传递过来。例如,open()函数将传递一个inode指针进来,表示目前打开的文件结点
    。需要注意的是,inode的成员已经被系统赋予了合适的值,驱动程序只需要使用该结
    点中的信息,而不用更改。Oepn()函数为:int (*open) (struct inode *, struct file *);

    inode结构中包含大量的有关文件的信息。这里,只对编写驱动程序有用的字段进行介
    绍,对于该结构更多的信息,可以参看内核源码。

      dev_t i_rdev,表示设备文件对应的设备号。

      struct list_head i_devices,如图6.2所示,该成员使设备文件连接到对应的cdev结
      构,从而对应到自己的驱动程序。
      struct cdev *i_cdev,如图6.2所示,该成员也指向cdev设备。
      除了从dev_t得到主设备号和次设备号外,这里还可以使用imajor()和iminor()函数从
      i_rdev中得到主设备号和次设备号。

    在Linux系统中,字符设备驱动程序由以下几个部分组成。

      字符设备加载和卸载函数

        在字符设备的加载函数中,应该实现字符设备号的申请和cdev的注册。相反,在字符设
        备的卸载函数中应该实现字符设备号的释放和cdev的注销。
        cdev是内核开发者对字符设备的一个抽象。除了cdev中的信息外,特定的字符设备还需
        要特定的信息,常常将特定的信息放在cdev之后,形成一个设备结构体,如代码中的
        xxx_dev。

      file_operations结构体和其成员函数

      驱动程序与应用程序的数据交换

    字符设备是3大类设备(字符设备、块设备、网络设备)中较简单的一类设备,其驱动
    程序中完成的主要工作是初始化、添加和删除cdev结构体,申请和释放设备号,以及填
    充file_operation结构体中操作函数,并实现file_operations结构体中的read()、
    write()、ioctl()等重要函数。如图6.4所示为cdev结构体、file_operations和用户空
    间调用驱动的关系。

  • 相关阅读:
    ASP.NET Core 搭配 Nginx 的真实IP问题
    【Spark】Sparkstreaming-性能调优
    【软件】软件设计-非功能性需求-性能-可靠性-安全性等
    【Spark】Spark-架构
    【Kafka】Kafka-副本-分区设置-性能调优
    【Spark】SparkStreaming-Tasks-数量如何设置?
    【Spark】Spark-性能调优-系列文章
    【Spark】Spark-空RDD判断与处理
    【Git】Git-add之后-忽略部分文件的方法
    【大数据】Spark-Hadoop-架构对比
  • 原文地址:https://www.cnblogs.com/chulin/p/10830111.html
Copyright © 2011-2022 走看看