Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1749638
  • 博文数量: 1647
  • 博客积分: 80000
  • 博客等级: 元帅
  • 技术积分: 9980
  • 用 户 组: 普通用户
  • 注册时间: 2008-10-13 15:15
文章分类

全部博文(1647)

文章存档

2011年(1)

2008年(1646)

我的朋友

分类:

2008-10-28 17:59:02


  Device Drivers (设备驱动程序)
  
  操作系统其中一个目的就是向用户掩盖系统硬件设备的特殊性。例如,虚拟文件系统呈现了安装的文件系统的一个统一的试图,而和底层的物理设备无关。本章描述 Linux 核心是如何管理系统中的物理设备的。
  
  CPU 不是系统中唯一的智能设备,每一个物理设备都由它自己的硬件控制器。键盘、鼠标和串行口由 SuperIO 芯片控制, IDE 磁盘由 IDE 控制器控制, SCSI 磁盘由 SCSI 控制器控制,等等。每一个硬件控制器都由自己的控制和状态控制器( CSR ),不同的设备之间是不同的。一个 Adaptec 2940 SCSI 控制器的 CSR 和 NCR 810 SCSI 控制器的完全不同。 CSR 用于启动和停止设备,初始化设备和诊断它的问题。管理这些硬件控制器的代码不是放在每一个应用程序里边,而是放在 Linux 核心。这些处理或者管理硬件控制器的软件脚做设备驱动程序。 Linux 核心的设备驱动程序本质上是特权的、驻留内存的低级的硬件控制例程的共享库。是 Linux 的设备驱动程序在处理它们管理的设备的特质。
  
  UNIX 的一个基本特点是它抽象了设备的处理。所有的硬件设备都象常规文件一样看待:它们可以使用和操作文件相同的、标准的系统调用来进行打开、关闭和读写。系统中的每一个设备都用一个设备特殊文件代表。例如系统中第一个 IDE 硬盘用 /dev/had 表示。对于块(磁盘)和字符设备,这些设备特殊文件用 mknod 命令创建,并使用主( major )和次( minor )设备编号来描述设备。也用设备特殊文件表达,但是它们由 Linux 在找到并初始化系统中的网络控制器的时候创建。同一个设备驱动程序控制的所有设备都由一个共同的 major 设备编号。次设备编号用于在不同的设备和它们的控制器之间进行区分。例如,主 IDE 磁盘的不同分区都由一个不同的次设备编号。所以, /dev/hda2 ,主 IDE 磁盘的第 2 个分区的主设备号是 3 ,而次设备号是 2 。 Linux 使用主设备号表和一些系统表(例如字符设备表 chrdevs )把系统调用中传递的设备特殊文件(比如在一个块设备上安装一个文件系统)映射到这个设备的设备驱动程序中。
  
  参见 fs/devices.c
  
  Linux 支持三类的硬件设备:字符、块和网络。字符设备直接读写,没有缓冲区,例如系统的串行端口 /dev/cua0 和 /dev/cua1 。块设备只能按照一个块(一般是 512 字节或者 1024 字节)的倍数进行读写。块设备通过 buffer cache 访问,可以随机存取,就是说,任何块都可以读写而不必考虑它在设备的什么地方。块设备可以通过它们的设备特殊文件访问,但是更常见的是通过文件系统进行访问。只有一个块设备可以支持一个安装的文件系统。通过 BSD socket 接口访问,网络子系统在网络章(第 10 章)描述。
  
  Linux 有许多不同的设备驱动程序(这也是 Linux 的力量之一)但是它们都具有一些一般的属性:
  
  Kernel code 设备驱动程序和核心中的其他代码相似,是 kenel 的一部分,如果发生错误,可能严重损害系统。一个写错的驱动程序甚至可能摧毁系统,可能破坏文件系统,丢失数据。
  
  Kenel interfaces 设备驱动程序必须向 Linux 核心或者它所在的子系统提供一个标准的接口。例如,终端驱动程序向 Linux 核心提供了一个文件 I/O 接口,而 SCSI 设备驱动程序向 SCSI 子系统提供了 SCSI 设备接口,接着,向核心提供了文件 I/O 和 buffer cache 的接口。
  
  Kernel mechanisms and services 设备驱动程序使用标准的核心服务例如内存分配、中断转发和等待队列来完成工作
  
  Loadable Linux 大多数的设备驱动程序可以在需要的时候作为核心模块加载,在不再需要的时候卸载。这使得核心对于系统资源非常具有适应性和效率。
  
  Configurable Linux 设备驱动程序可以建立在核心。哪些设备建立到核心在核心编译的时候是可以配置的。
  
  Dynamic 在系统启动,每一个设备启动程序初始化的时候它查找它管理的硬件设备。如果一个设备驱动程序所控制的设备不存在并没有关系。这时这个设备驱动程序只是多余的,占用很少的系统内存,而不会产生危害。
  
  8.1 Poling and Interrupts (轮询和中断)
  
  每一次给设备命令的时候,例如“把读磁头移到软盘的第 42 扇区“,设备驱动程序可以选择它如何判断命令是否执行结束。设备驱动程序可以轮询设备或者使用中断。
  
  轮询设备通常意味着不断读取它的状态寄存器,直到设备的状态改变指示它已经完成了请求。因为设备驱动程序是核心的一部分,如果驱动程序一直在轮询,核心在设备完成请求之前不能运行其他任何东西,会是损失惨重的。所以轮询的设备驱动程序使用一个系统计时器,让系统在晚些时候调用设备驱动程序中的一个例程。这个定时器例程会检查命令的状态, Linux 的软盘驱动程序就是这样工作的。使用计时器进行轮询是一种最好的接近,而更加有效的方法是使用中断。
  
  中断设备驱动程序在它控制的硬件设备需要服务的时候会发出一个硬件中断。例如:一个以太网设备驱动程序会在设备在网络上接收到一个以太网报文的时候被中断。 Linux 核心需要有能力把中断从硬件设备转发到正确的设备驱动程序。这通过设备驱动程序向核心登记它所使用的中断来实现。它登记中断处理程序例程的地址和它希望拥有的中断编号。你通过 /proc/interrupts 可以看到设备驱动使用了哪些中断和每一类型的中断使用了多少次:
  
  0: 727432 timer
  
  1: 20534 keyboard
  
  2: 0 cascade
  
  3: 79691 + serial
  
  4: 28258 + serial
  
  5: 1 sound blaster
  
  11: 20868 + aic7xxx
  
  13: 1 math error
  
  14: 247 + ide0
  
  15: 170 + ide1
  
  对于中断资源的请求发生在驱动程序初始化的时间。系统中的一些中断是固定的,这是 IBM PC 体系结构的遗留物。例如软驱磁盘控制器总是用中断 6 。其他中断,例如 PCI 设备的中断,在启动的时候动态分配。这时设备驱动程序必须首先找出它所控制的设备的中断号,然后才能请求拥有这个中断(的处理权)。对于 PCI 中断, Linux 支持标准的 PCI BIOS 回调( callback )来确定系统中设备的信息,包括它们的 IRQ 。
  
  一个中断本身是如何转发到 CPU 依赖于体系结构。但是在大多数的体系上,中断都用一种特殊的模式传递,而停止系统中发生其他中断。设备驱动程序在它的中断处理例程中应该做尽可能少的工作,使得 Linux 核心可以结束中断并返回到它中断之前的地方。收到中断后需要做大量工作的设备驱动程序可以使用核心的 bottom half handler 或者任务队列把例程排在后面,以便在以后调用。
  
  8.2 Direct Memory Access ( DMA )
  
  当数据量比较少的时候用中断驱动的设备驱动程序向设备或者通过设备传输数据工作地相当好。例如,一个 9600 波特率的 modem 每一毫秒( 1/1000 秒)大约可以传输一个字符。如果中断延迟,就是从硬件设备发出中断到开始调用设备驱动程序中的中断处理程序所花的时间比较少(比如 2 毫秒),那么数据传输对系统整体的映像就非常小。 9600 波特率的 modem 数据传出只会占用 0.002% 的 CPU 处理时间。但是对于高速的设备,比如硬盘控制器或者以太网设备,数据传输速率相当高。一个 SCSI 设备每秒可以传输高达 40M 字节的信息。
  
  直接内存存取,或者说 DMA ,就是发明来解决这个问题的。一个 DMA 控制器允许设备不需要处理器的干预而和系统内存创树数据。 PC 的 ISA DMA 控制器由 8 个 DMA 通道,其中 7 个可用于设备驱动程序。每一个 DMA 通道都关联一个 16 位的地址寄存器和一个 16 位的计数寄存器( count register )。为了初始化一次数据传输,设备驱动程序需要建立 DMA 通道的地址和计数寄存器,加上数据传输的方向,读或写。当传输结束的时候,设备中断 PC 。这样,传输发生的时候, CPU 可以作其他事情。
  
  使用 DMA 的时候设备驱动程序必须小心。首先,所有的 DMA 控制器都不了解虚拟内存,它只能访问系统中的物理内存。因此,需要进行 DMA 传输的内存必须是物理内存中连续的块。这意味着你不能对于一个进程的虚拟地址空间进行 DMA 访问。但是你可以在执行 DMA 操作的时候把进程的物理也锁定到内存中。第二: DMA 控制器无法访问全部的物理内存。 DMA 通道的地址寄存器表示 DMA 地址的首 16 位,跟着的 8 位来自于页寄存器( page register )。这意味着 DMA 请求限制在底部的 16M 内存中。
  
  DMA 通道是稀少的资源,只有 7 个,又不能在设备驱动程序之间共享。象中断一样,设备驱动程序必须有能力发现它可以使用哪一个 DMA 通道。象中断一样,一些设备有固定的 DMA 通道。比如软驱设备,总是用 DMA 通道 2 。有时,设备的 DMA 通道可以用跳线设置:一些以太网设备用这种技术。一些更灵活的设备可以告诉它(通过它们的 CSR )使用哪一个 DMA 通道,这时,设备驱动程序可以简单地找出一个可用的 DMA 通道。
  
  Linux 使用 dma_chan 数据结构向量表(每一个 DMA 通道一个)跟踪 DMA 通道的使用。 Dma_chan 数据结构只有两个玉:一个字符指针,描述这个 DMA 通道的属主,一个标志显示这个 DMA 通道是否被分配。当你 cat /proc/dma 的时候显示的就是 dma_chan 向量表。
  
  8.3 Memory (内存)
  
  设备驱动程序必须小心使用内存。因为它们是 Linux 核心的一部分,它们不能使用虚拟内存。每一次设备驱动程序运行的时候,可能是接收到了中断或者调度了一个 buttom half handler 或任务队列,当前的进程都可能改变。设
【责编:admin】

--------------------next---------------------

阅读(160) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~