《Linux设备驱动》读书笔记:第一章:设备驱动简介

来源:互联网 发布:js代码封装 编辑:程序博客网 时间:2024/06/05 10:43

1 驱动程序的角色

驱动程序需要实现“机制”,而不是实现“策略”。

  • 机制:提供了什么能力
  • 策略:如何使用这些能力

    比如说,一个软驱驱动需要是策略无关的,它的任务是把软盘展示为连续的数据块集合;而策略由系统高层提供,比如说,谁可以访问软驱,是直接访问还是通过文件系统访问,用户是否可以挂接软驱上的文件系统等。

   编写驱动程序时,应该注意一个基本概念:编写访问硬件的内核代码,但是不能向用户强加任何策略,因为不同用户的需求是不同的。驱动程序的任务是让硬件可用,而如何使用硬件的问题应该留给应用程序处理。然而,有时还是需要作一些策略选择。比如说,数字I/O驱动可能只提供对硬件的字节宽度的访问,以避免需要额外的代码来处理单独的二进制位。

    驱动是位于应用程序和实际硬件间的软件层,这使得驱动程序可以选择设备是如何展现的:对于同样的设备,不同的驱动可以提供不同的能力。编写驱动程序时需要平衡各方面的考虑。比如说,不同程序可以并发使用单个设备,驱动程序完全可以自由确定如何处理并发:可以不依赖硬件能力而在设备上实现内存映射,也可以提供用户库以帮助应用程序开发者在可用的原语上实现新的策略。在为用户提供尽可能多的选项和所拥有的用于编写驱动程序的时间,以及保持代码简单、避免引入错误之间做出平衡是需要考虑的一个主要方面。

    策略无关的驱动程序通常具有一些典型特征。比如说,支持同步和异步操作、具有多次打开的能力、可以使用硬件的所有功能、没有用于简化操作的软件层,也不提供策略相关的操作。具有这些特征的驱动程序不仅便于最终用户使用,也易于编写和维护。

    很多驱动程序通常与帮助用户配置和访问目标设备的程序一起发布。通常还会提供客户端库,用于提供不需要作为驱动程序来实现的额外能力。本书讨论的范围是内核,所以不会讨论策略问题或者应用程序和支持库。但是应该知道,用户程序是(驱动程序)软件包的一个组成部分。即使是策略无关的软件包,也是与在底层机制上实现默认行为的配置文件一起发布的。

2 内核划分

  1. 进程管理:创建和销毁进程,处理进程与外部世界的连接(输入和输出)、进程间通信、进程调度等。
  2. 内存管理:内存是系统的主要资源之一,内存管理策略对于系统性能至关重要。
  3. 文件系统:内核在非结构化的硬件上实现了结构化的文件系统。Unix中几乎任何东西都可以作为文件进行处理。
  4. 设备控制:几乎每个系统操作最终都映射到一个物理设备上。除了处理器、内存和少量其他实体外,所有设备控制操作都是由特定于设备的代码实现的,这些代码就是设备驱动程序。内核必须为系统中存在的每个外设嵌入设备驱动程序。设备控制是本书主要关注的内核功能。
  5. 网络:网络必须由操作系统管理,因为报文分组是异步到达的,大多数网络操作都不是特定于一个进程的。系统需要处理数据分组在程序和网络接口间的传递问题,必须根据网络情况控制程序的执行(流量控制)。此外,路由和地址解析也由内核实现。

3 可装载模块

  可在运行时扩展内核提供的功能是Linux的优秀特性之一。这意味着在系统已经装载和正在运行时可以向内核添加功能,当然也可以移除功能。
  可以在运行时添加到内核的代码片段称为模块。Linux内核支持多种不同类型的模块,包括但不限于设备驱动程序。模块由代码构成,可以通过insmod命令动态链接到运行中的内核,也可以用rmmod命令从运行中的内核移除模块。

4 设备和模块分类

三种类型的设备和驱动模块:

  1. 字符设备
    字符设备是可以作为字节流进行访问的设备
    ,实现对设备的字节流方式访问是字符设备驱动的任务。字符设备驱动至少需要实现openclosereadwrite 系统调用。文本控制台(/dev/console)和串口(/dev/ttyS0等)是字符设备的实例,因为它们完美地被流抽象表示。字符设备可以通过文件系统节点,如/dev/tty1和/dev/lp0,进行访问。字符设备和普通文件的唯一不同只是,总是可以在普通文件中来回移动,但字符设备仅仅是数据通道,只能顺序访问。然而,也存在可以看做数据区域、可以在其中来回移动的字符设备。例如,帧捕捉器通常实现了这种能力,使得应用程序可以使用mmap或者lseek访问已经获取的整个图像。
  2. 块设备
    跟字符设备一样,块设备也是通过/dev目录中的文件系统节点进行访问的。块设备是可以在其上驻留文件系统的设备,比如说,磁盘。在很多Unix系统中,块设备只能处理传输一个或者多个完整块的I/O操作。块长度通常为512字节。Linux却允许像字符设备那样读写块设备,即允许一次传输任意字节数。这样,块设备和字符设备的唯一差别仅仅是内核管理其数据的方式不同,以及由此带来的内核/驱动软件接口的差异。和字符设备一样,每个块设备都可以通过文件系统节点进行访问,它们之间的不同对用户是透明的。然而,块设备到内核的接口是完全不同于字符设备的。
  3. 网络接口
    任何网络传输都是通过网络接口进行的。网络接口是可以与其他主机交换数据的设备。通常网络接口是一个硬件设备,然而也可以是像环回接口这样的纯软件设备。网络接口由内核的网络子系统驱动,负责收发数据分组,但网络接口不知道单次传输是如何映射到被传输的分组的。很多网络连接(特别是使用TCP的)通常是面向流的,但网络设备通常是围绕收发分组进行设计的。网络驱动只处理分组,不知道网络连接的存在。
    因为不是面向流的设备,所以网络接口不能像/dev/tty1那样容易地映射到文件系统中的某个节点。Unix提供的对网络接口的访问方法仍然是为其分配唯一的名字(如eth0),但是分配的名字不对应文件系统中的实体。内核和网络设备驱动间的通信方式是完全不同于字符和块设备驱动的:不使用read和write,而是调用与分组传输相关的函数。

    也存在不同于上面论述的设备类型分类的其他的设备驱动分类方式。通常,一些与额外的内核层次一起工作的驱动程序会支持某种类型设备的功能。比如说,USB模块、串口模块、SCSI模块等等。每个USB设备由与USB子系统一同工作的一个USB模块驱动,但是设备本身在系统中表现为一个字符设备(如USB串口)、块设备(如USB读卡器)或者网络设备(如USB以太网接口)。

    近年来内核中加入了其他类型的设备驱动,如火线驱动和I2O驱动。与处理USB和SCSI驱动的方式一样,内核开发者收集了这类设备的特征,提供给驱动开发者,以避免重复的工作和引入问题,因而简化和加强了编写这类驱动的过程。

    除了设备驱动外,内核中其他一些硬件和软件功能也被模块化,最常见的例子就是文件系统。文件系统决定如何在块设备上组织信息来代表目录和文件树。这种实体不是设备驱动,因为没有哪个设备与信息的组织方式相关。文件系统是一种软件驱动,因为它把底层数据结构映射到高层数据结构。文件系统决定文件名可以是多长的,以及在目录中存储每个文件的哪些信息。文件系统模块必须通过映射文件名和路径(以及其他信息,如访问模式)到存储在数据块中的数据结构的方式,来实现访问目录和文件的最底层系统调用。这种接口完全与磁盘(或者其他媒体)数据传输无关,数据传输是由块设备驱动完成的。

    解码文件系统信息的能力位于内核的最底层,这种能力是极其重要的。为新的CD-ROM编写的块设备驱动,如果不能在其容纳的数据上运行ls或者cp命令,那么是无用的。Linux支持文件系统模块的概念。文件系统模块的软件接口声明了可以在文件系统节点、目录、文件和超级块上执行的不同操作。程序员很少需要编写文件系统模块,因为官方的内核已经包含大多数重要文件系统类型的代码。

安全问题

    安全检查是由内核代码实施的。在官方发布的内核中,只有授权的用户可以加载模块,系统调用init_module会检查进程是否有向内核加载模块的权限。通常来说,应该避免将安全策略编码到驱动程序中。安全是一种策略,应该在系统管理员的控制下,由内核的更高层级处理。
    然而,应该对会影响整个系统的设备访问操作提供足够的控制。比如说,影响全局资源的操作(如设置中断线)、会损坏硬件的操作(如载入固件),或者会影响其他用户的操作(如设置磁带的默认块尺寸)。这些操作通常只对具有足够权限的用户可用,还应该在驱动程序中进行权限检查。
    当然,还应该避免引入安全问题,比如说,缓冲区溢出。检查来自用户进程的所有输入;注意处理未初始化的内存,在传递从内核取得的内存到用户进程前,应该清零或者初始化,以避免信息泄露。对于会影响系统的特定操作(如在适配板上加载固件或者格式化磁盘),应该严格限制为只有授权用户才可以进行。注意来自第三方的软件,特别是内核相关的。
    Linux内核可以编译为不支持模块,这样可以避免模块相关的安全漏洞。

版本号
   本书基于2.6.10版本的内核。所有示例代码都至少在x86和x86-64平台上测试过。因为在32位和64位平台上测试过,所以示例代码应该可以在所有其他平台上正确编译和运行。


菊子曰 本文用菊子曰发布