下一页 上一页 目录

2. PnP 应该做什么:分配“总线资源”

2.1 什么是即插即用 (PnP)?

如果您不理解本节,请阅读下一节 硬件设备及其通信

简而言之,即插即用告诉软件(设备驱动程序)在哪里可以找到各种硬件(设备),例如调制解调器、网卡、声卡等。即插即用的任务是将物理设备与操作它们的软件(设备驱动程序)进行匹配,并在每个物理设备及其驱动程序之间建立通信通道。为了实现这一点,PnP 在硬件中分配和设置以下“总线资源”:I/O 地址、内存区域、IRQ、DMA 通道(仅限 LPC 和 ISA 总线)。这 4 件事有时被称为“一级资源”或简称为“资源”。PnP 维护其所做事情的记录,并允许设备驱动程序获取此信息。如果您不理解这 4 个总线资源是什么,请阅读本 HOWTO 的以下小节:I/O 地址、IRQ、DMA 通道、内存区域。Linux Gazette 上一篇关于其中 3 个总线资源的文章是 IRQ、DMA 和基地址简介。一旦这些总线资源被分配(并且如果安装了正确的驱动程序),实际的驱动程序及其在 /dev 目录中的“文件”就可以使用了。

PnP 分配总线资源有时被称为“配置”,但这只是一种低级别的配置类型。/etc 目录中有许多配置文件,但几乎所有配置文件都不是用于 PnP 配置的。因此,大多数硬件设备的配置与 PnP 或总线资源无关。例如,通过“初始化字符串”初始化调制解调器或设置其速度不是 PnP。因此,在谈论 PnP 时,“配置”仅表示某种类型的配置。虽然其他文档(例如 MS Windows 的文档)只是将总线资源称为“资源”,但我有时使用术语“总线资源”而不是仅仅“资源”,以便将其与大量其他类型的资源区分开来。

PnP 是一个由各种软件和硬件完成的过程。如果只有一个程序在 Linux 中处理 PnP,那将很简单。但是对于 Linux,每个设备驱动程序都使用内核提供的软件来执行自己的 PnP。PC 的 BIOS 硬件在 PC 首次启动时执行 PnP。而且还有更多内容。

2.2 硬件设备及其通信

计算机由 CPU/处理器(用于计算)和 RAM 内存(用于存储程序和数据,以便快速访问)组成。此外,还有许多设备,例如各种类型的磁盘驱动器、显卡、键盘、网络设备、调制解调器卡、声卡、USB 总线、串行和并行端口等。在过去,大多数设备都在插入 PC 插槽的卡上。如今,许多以前是卡的设备现在都在板载,因为它们包含在主板上的芯片中。还有一个电源来提供电能,主板上的各种总线将设备连接到 CPU,以及一个机箱来容纳所有这些。

插入主板的卡可能包含多个设备。内存芯片有时也被认为是设备,但在本 HOWTO 中使用的意义上,它们不是即插即用的。

为了使计算机系统正常工作,每个设备都必须在其“设备驱动程序”的控制下。这是软件,它是操作系统的一部分(可能作为模块加载)并在 CPU 上运行。设备驱动程序与 /dev 目录中的“特殊文件”相关联,尽管它们实际上不是文件。它们有诸如 hda3(硬盘驱动器 a 上的第三个分区)、ttyS1(第二个串行端口)、eth0(第一个以太网卡)等名称。

eth0 设备用于以太网卡(网卡)。以前它是 /dev/eth0,但现在它只是内核中的一个虚拟设备。eth0 指的是什么取决于您拥有的以太网卡的类型。如果驱动程序是一个模块,则此分配很可能在内核内部表中,但可能在 /etc/modules.conf 中找到(称为“别名”)。例如,如果您有一个使用“tulip”芯片的以太网卡,您可以将“alias eth0 tulip”放入 /etc/modules.conf 中,以便当您的计算机请求 eth0 时,它可以找到 tulip 驱动程序。但是,现代内核通常可以找到正确的驱动程序模块,因此您很少需要自己指定它。

为了控制设备,CPU(在设备驱动程序的控制下)向各种设备发送命令和数据,并从其中读取状态和数据。为了做到这一点,每个设备驱动程序都必须知道它控制的设备的地址。知道这样的地址相当于建立了一个通信通道,即使物理“通道”实际上是 PC 内部的数据总线,它与其他许多设备共享。

这种通信通道实际上比上面描述的要复杂一些。“地址”实际上是一个地址范围,因此有时使用“范围”而不是“地址”这个词。甚至可能存在一个设备的多个范围(没有重叠)。此外,通道还有一个反向部分(称为中断),允许设备向其设备驱动程序发送紧急的“帮助”请求。

2.3 地址

PCI 总线有 3 个地址空间:I/O、主内存(IO 内存)和配置。旧的 ISA 总线缺少真正的“配置”地址空间。只有 I/O 和 IO 内存空间用于设备 IO。配置地址是固定的,无法更改,因此不需要分配。有关更多详细信息,请参阅 PCI 配置地址空间

当 CPU 想要访问设备时,它会将设备的地址放在计算机的主要总线(对于 PCI:地址/数据总线)上。所有类型的地址(例如 I/O 和主内存)共享 PC 内部的同一总线。但是,PC 总线中某些专用线路上的电压存在与否表明地址位于哪个“空间”中:I/O、主内存、(请参阅 内存范围)或配置(仅限 PCI)。这有点过于简化了,因为告诉 PCI 设备它是配置空间访问实际上比上面描述的更复杂。有关详细信息,请参阅 PCI 配置地址空间。有关寻址的更多详细信息,请参阅 地址详细信息

设备的地址存储在其物理设备中的寄存器中。它们可以被软件更改,也可以被禁用,从而使设备完全没有地址。但 PCI 配置地址无法更改或禁用。

2.4 I/O 地址(原理也适用于其他资源)

设备最初位于 I/O 地址空间中,但今天它们可能使用主内存中的空间。I/O 地址有时简称为“I/O”、“IO”、“i/o”或“io”。也使用术语“I/O 端口”或“I/O 范围”。不要将这些 IO 端口与位于主内存中的“IO 内存”混淆。分配 I/O 地址(或某些其他总线资源,例如 ISA 总线上的中断)主要有两个步骤

  1. 在硬件(其寄存器之一)中设置 I/O 地址等
  2. 让其设备驱动程序知道此 I/O 地址等是什么

通常,设备驱动程序会同时执行这两项操作(某种程度上)。如果设备驱动程序发现地址先前已设置(可能是由 BIOS 设置的)并且愿意接受该地址,则设备驱动程序实际上不需要设置 I/O 地址。一旦驱动程序找到先前设置的地址或自行设置地址,那么它显然知道地址是什么,因此无需让驱动程序知道地址——它已经知道了。

上面的两个步骤过程(1. 在硬件中设置地址。2. 让驱动程序知道它。)有点像在街道上找到某人的门牌号的两部分问题。有人必须在房屋正面安装一个号码,以便可以找到它,然后可能想去这个地址的人必须获取(并写下)这个门牌号,以便他们可以找到房子。对于计算机,设备硬件必须首先将其地址放入其硬件中的特殊寄存器中(贴上门牌号),然后设备驱动程序必须获取此地址(将门牌号写在其地址簿中)。这两者都必须完成,要么由软件自动完成,要么通过手动将数据输入到配置文件中来完成。当只有其中一个步骤正确完成时,可能会出现问题。

对于手动 PnP 配置,有些人犯了只执行这两个步骤之一的错误,然后想知道为什么计算机找不到设备。例如,他们可能会使用“setserial”为串行端口分配地址,但没有意识到这只是告诉驱动程序一个地址。它不会在串行端口硬件本身中设置地址。如果您告诉驱动程序错误的信息,那么您就麻烦了。告诉驱动程序的另一种方法是将地址作为内核模块(设备驱动程序)的选项给出。如果您告诉它的内容是错误的,则可能会出现问题。一个智能的驱动程序可能会检测到硬件的实际设置方式,并拒绝选项提供的错误信息(或者至少发出错误消息)。

一个显而易见的要求是,在设备驱动程序可以使用地址之前,必须首先在物理设备(例如卡)中设置地址。由于设备驱动程序通常在您启动计算机后不久启动,因此它们有时会在 PnP 配置程序在卡中设置地址之前尝试访问卡(以查看它是否在那里等)。然后您会看到一条错误消息,提示他们找不到该卡,即使它在那里(但尚未有地址)。

最后几段关于 I/O 地址的内容同样适用于大多数其他总线资源:内存范围IRQ --概述DMA 通道。这些内容将在接下来的 3 节中解释。例外情况是 PCI 总线上的中断不是由卡寄存器设置的,而是由主板上的芯片路由(映射)到 IRQ 的。然后,PCI 卡路由到的 IRQ 被写入卡上的寄存器,仅供参考。

要查看您的 PC 上使用了哪些 IO 地址,请查看 /proc/ioports 文件。

2.5 内存范围

许多设备被分配了主内存中的地址空间。它有时被称为“共享内存”或“内存映射 IO”或“IO 内存”。此内存物理上位于物理设备内部,但计算机访问它的方式就像访问内存芯片上的内存一样。在讨论总线资源时,它通常简称为“内存”、“mem”或“iomem”。除了使用这种“内存”外,这样的设备还可能使用传统的 IO 地址空间。要查看您的计算机上正在使用的 mem,请查看 /proc/iomem。此“文件”包括您的普通 RAM 内存芯片使用的内存,因此它显示的是一般的内存分配,而不仅仅是 iomem 分配。如果您看到一个奇怪的数字而不是名称,则它很可能是 PCI 设备的编号,您可以通过键入“lspci”来验证。

当您插入使用 iomem 的卡时,实际上您也在插入一个用于主内存的内存模块。PnP 为其选择了一个高地址,以便它不会与主内存模块(芯片)冲突。此内存可以是 ROM(只读内存)或共享内存。共享内存是在设备和 CPU(运行设备驱动程序)之间共享的,就像 IO 地址空间在设备和 CPU 之间共享一样。此共享内存充当设备和主内存之间数据“传输”的手段。它是输入-输出 (IO),但它不是在 IO 空间中完成的。卡和设备驱动程序都需要知道内存范围。

卡上的 ROM(只读内存)是一种不同类型的 iomem。它很可能是一个程序(可能是设备驱动程序),将与该设备一起使用。它可能是初始化代码,因此仍然需要设备驱动程序。希望它可以在 Linux 上运行,而不仅仅是 MS Windows。它可能需要被阴影化,这意味着它被复制到您的主内存芯片中以便更快地运行。一旦被阴影化,它就不再是“只读”的了。

2.6 IRQ --概述

阅读完本文后,您可能想阅读 中断 --详细信息 以了解更多详细信息。以下内容是有意简化的:除了地址之外,还需要处理中断号(例如 IRQ 5)。它被称为 IRQ(中断请求)号或简称为“irq”。我们已经在上面提到,设备驱动程序必须知道卡的地址才能与之通信。

但是反方向的通信呢?假设设备需要立即告诉其设备驱动程序某些内容。例如,设备可能正在接收大量目标为主内存的字节,并且其用于存储这些字节的缓冲区几乎已满。因此,设备需要告诉其驱动程序立即获取这些字节,以防止缓冲区因传入的字节流而溢出。另一个示例是向驱动程序发出信号,表明设备已完成发送一批字节,现在正在等待驱动程序的更多字节,以便它也可以发送它们。

设备应如何快速向其驱动程序发出信号?它可能无法使用主数据总线,因为它很可能已经在使用中。相反,它在一条专用中断线(也称为线路或迹线)上施加电压,这条线路通常专用于该设备。这种电压信号称为中断请求 (IRQ) 或简称为“中断”。PC 中有相当于 16 条(或 24 条等)这样的线路,每条线路(间接地)通向特定的设备驱动程序。每条线路都有一个唯一的 IRQ(中断请求)号。设备必须将其中断放在正确的线路上,设备驱动程序必须在正确的线路上侦听中断。设备发送此类“帮助请求”的线路由存储在设备中的 IRQ 号确定。设备驱动程序必须知道相同的 IRQ 号,以便设备驱动程序知道要侦听哪个 IRQ 线路。

一旦设备驱动程序从设备收到中断,它必须查明发出中断的原因,并采取适当的措施来处理中断。在 ISA 总线上,每个设备通常需要其自己唯一的 IRQ 号。对于 PCI 总线和其他特殊情况,允许共享 IRQ(两个或多个 PCI 设备可能具有相同的 IRQ 号)。此外,对于 PCI,每个 PCI 设备都有一条固定的“PCI 中断”线路。但是,可编程路由芯片将 PCI 线路映射到 ISA 类型的中断。有关上述所有工作原理的详细信息,请参阅 中断 --详细信息

2.7 DMA(直接内存访问)或总线控制

对于 PCI 总线,DMA 和总线控制意味着相同的事情。在 PCI 总线之前,总线控制很少见,DMA 的工作方式不同且速度较慢。直接内存访问 (DMA) 是指允许设备从 CPU 接管主计算机总线并将字节直接传输到主内存或某些其他设备。通常,CPU 会在两步过程中进行从设备到主内存的传输

  1. 从设备的 I/O 内存空间读取一块字节,并将这些字节放入 CPU 本身
  2. 将这些字节从 CPU 写入主内存

使用 DMA,这是一个将字节直接从设备发送到内存的一步过程。设备必须在其硬件中内置 DMA 功能,因此并非所有设备都可以执行 DMA。在 DMA 进行期间,CPU 不能做太多事情,因为主总线正在被 DMA 传输使用。

旧的 ISA 总线可以执行慢速 DMA,而 PCI 总线通过总线控制执行“DMA”。LPC 总线既有旧的 DMA,也有新的 DMA(总线控制)。在 PCI 总线上,更准确地应该称为“总线控制”的通常被称为“Ultra DMA”、“BM-DNA”、“udma”或简称为“DMA”。总线控制允许设备临时成为总线主控器,并传输字节,几乎就像总线主控器是 CPU 一样。它不使用任何通道号,因为 PCI 总线的组织方式使得 PCI 硬件知道哪个设备当前是总线主控器,哪个设备正在请求成为总线主控器。因此,PCI 总线没有 DMA 通道的资源分配,并且此总线不存在 dma 通道资源。LPC(低引脚数)总线应该由 BIOS 配置,因此用户无需担心其 DMA 通道。

2.8 DMA 通道(不适用于 PCI 总线)

这仅适用于 LPC 总线和旧的 ISA 总线。当设备想要执行 DMA 时,它会使用专用的 DMA 请求线路发出 DMA 请求,很像中断请求。DMA 实际上可以通过使用中断来处理,但这会引入一些延迟,因此通过使用称为 DMA 请求的特殊类型的中断来完成 DMA 更快。与中断一样,DMA 请求也被编号,以便识别哪个设备正在发出请求。此号码称为 DMA 通道。由于 DMA 传输都使用主总线(并且一次只能运行一个),因此它们实际上都使用相同的通道进行数据流,但“DMA 通道”号用于标识谁在使用“通道”。主板上存在硬件寄存器,用于存储每个“通道”的当前状态。因此,为了发出 DMA 请求,设备必须知道其 DMA 通道号,该号码必须存储在物理设备上的特殊寄存器中。

2.9 设备和驱动程序的“资源”

因此,设备驱动程序必须以某种方式“附加”到它们控制的硬件。这是通过为物理设备分配总线资源(I/O、内存、IRQ、DMA)并让设备驱动程序了解它来完成的。例如,串行端口仅使用 2 个资源:IRQ 和 I/O 地址。这两个值都必须提供给设备驱动程序和物理设备。驱动程序(及其设备)还在 /dev 目录中获得一个名称(例如 ttyS1)。地址和 IRQ 号由物理设备存储在其卡上的配置寄存器中(或主板上的芯片中)。20 世纪 90 年代中期的旧硬件使用开关(或跳线)来物理设置硬件中的 IRQ 和地址。此设置保持固定,直到有人卸下计算机的盖子并移动跳线。

但是对于 PnP 的情况(没有跳线),当 PC 断电(关闭)时,配置寄存器数据通常会丢失,因此每次 PC 开机时都必须为每个设备重新提供总线资源数据。

2.10 资源是有限的

理想的计算机

PC 的架构仅提供有限数量的资源:IRQ、DMA 通道、I/O 地址和内存区域。如果只有有限数量的设备,并且它们都使用标准化的总线资源值(例如唯一的 I/O 地址和 IRQ 号),则将设备驱动程序附加到设备将不会有问题。每个设备都将具有固定的资源,这些资源不会与您计算机上的任何其他设备冲突。没有两个设备会具有相同的地址,ISA 总线上不会出现 IRQ 冲突等。每个驱动程序都将使用唯一的地址、IRQ 等进行编程,这些地址、IRQ 等都硬编码到程序中。生活将变得简单。

防止地址冲突的另一种方法是将每个卡的插槽号作为地址的一部分包含在内。因此,两个不同的卡之间可能不会发生地址冲突(因为它们位于不同的插槽中)。卡设计不允许卡的不同功能之间发生地址冲突。事实证明,配置地址空间(用于资源查询和分配)实际上是这样做的。但这不适用于 I/O 地址和内存区域。像 PCI 总线上那样共享 IRQ 也可以避免冲突,但可能会引起其他问题。

真实的计算机

但是 PC 架构存在冲突问题。设备数量的增加(包括同一类型的多个设备)趋于增加潜在的冲突。与此同时,PCI 总线的引入(其中两个或多个设备可以共享同一个中断)以及更多中断的引入,趋于减少冲突。由于转向 PCI,总体结果是冲突减少,因为最稀缺的资源是 IRQ。但是,即使在 PCI 总线上,避免 IRQ 共享也更有效。在某些中断快速连续发生并且必须快速响应的情况下(例如音频),共享可能会导致性能下降。因此,将所有 PCI 设备分配到同一个 IRQ 并不好,分配需要平衡。然而,有些人发现他们所有的 PCI 设备都在同一个 IRQ 上。

因此,设备需要具有一定的灵活性,以便可以将它们设置为避免任何冲突并实现平衡所需的任何地址、IRQ 等。但是,某些 IRQ 和地址是非常标准的,例如时钟和键盘的 IRQ 和地址。这些不需要这种灵活性。

除了总线资源分配冲突的问题外,还存在告诉设备驱动程序总线资源是什么时出错的问题。对于旧式手动配置的情况,用户将使用的资源键入存储在硬盘驱动器上的配置文件中,这种情况更有可能发生。当资源通过卡上的跳线设置时,这通常可以正常工作(前提是用户知道它们的设置方式并且在将此数据键入配置文件时没有犯任何错误)。但是,由于资源是由 PnP 软件设置的,因此它们可能并不总是设置相同,这可能意味着对于任何手动配置(用户在其中键入由 PnP 设置的总线资源值)都会遇到麻烦。

如果正确完成总线资源的分配,则会在物理硬件及其设备驱动程序之间建立非冲突的通信通道。例如,如果某个 I/O 地址范围(资源)被分配给设备驱动程序和硬件,那么这就在它们之间建立了一个单向通信通道。驱动程序可能会向设备发送命令和其他信息。实际上,这不仅仅是单向通信,因为驱动程序可以通过读取设备的寄存器来从设备获取信息。但是设备无法通过这种方式发起任何通信。要发起通信,设备需要一个 IRQ,以便它可以向其驱动程序发送中断。这创建了一个双向通信通道,驱动程序和物理设备都可以发起通信。

2.11 PnP 第二次介绍

术语即插即用 (PnP) 有多种含义。在广义上,它只是自动配置,即只需插入设备,它就会自行配置。在本 HOWTO 中使用的意义上,PnP 意味着配置 PnP 总线资源(在物理设备中设置它们)并让设备驱动程序了解它。对于 Linux 的情况,通常只是驱动程序确定 BIOS 如何设置总线资源,并在必要时,驱动程序发出命令来更改(重置)总线资源。“PnP”通常仅表示 ISA 总线上的 PnP,因此来自 isapnp 的消息:“未找到即插即用设备”仅表示未找到 ISA PnP 设备。标准 PCI 规范(在创造术语“PnP”之前发明)为 PCI 总线提供了等效于 PnP 的功能。

PnP 将设备与其设备驱动程序匹配,并通过分配总线资源来指定其通信通道。它使用标准化协议与位于物理设备内部的配置寄存器进行电子通信。在即插即用之前的 ISA 总线上,总线资源以前是通过跳线或开关在硬件设备中设置的。有时可以通过驱动程序(通常仅为 MS OS 编写,但在极少数情况下受 Linux 驱动程序支持)以电子方式将总线资源设置到硬件中。这有点像 PnP,但没有使用标准化的协议,因此它实际上不是 PnP。某些卡具有跳线设置,这些跳线设置可以被此类软件覆盖。对于 PnP 之前的 Linux,大多数软件驱动程序通过配置文件(或类似文件)或通过探测设备预期驻留的地址来分配总线资源。但是,这些方法今天仍在使用,以允许 Linux 使用旧的非 PnP 硬件。有时,这些旧方法今天仍然在 PnP 硬件上使用(例如,在 BIOS 通过 PnP 方法为硬件分配资源之后)。

PCI 总线从一开始就类似于 PnP,但它通常不被称为 PnP 或“即插即用”,结果 PnP 通常意味着 ISA 总线上的 PnP。但是本文档中的 PnP 通常意味着 ISA 或 PCI 总线上的 PnP。

2.12 PnP 如何工作(简化)

以下是 PnP 在理论上应该如何工作的。假设的 PnP 配置程序查找所有 PnP 设备,并询问每个设备它需要什么总线资源。然后,它检查它有哪些总线资源(IRQ 等)可以提供。当然,如果它保留了非 PnP(传统)设备使用的总线资源(如果它知道它们),则不会提供这些资源。然后,它使用一些标准(PnP 规范未指定)来分配总线资源,以便没有冲突,并且所有设备都能获得它们需要的资源(如果可能)。然后,它间接地告诉每个物理设备分配给它的总线资源,并且设备自行设置以仅使用分配的总线资源。然后,设备驱动程序以某种方式找出其设备使用的总线资源,从而能够有效地与其控制的设备进行通信。

例如,假设一张卡需要一个中断(IRQ 号)和 1 MB 的共享内存。PnP 程序从卡上的配置寄存器读取此请求。然后,它为该卡分配 IRQ5 和 1 MB 的内存地址空间,起始地址为 0xe9000000。PnP 程序还从卡上读取识别信息,告知其设备类型、ID 号等。然后,它直接或间接地告诉相应的设备驱动程序它所做的事情。如果是驱动程序本身正在执行 PnP,则无需为设备查找驱动程序(因为它的驱动程序已经在运行)。否则,需要找到合适的设备驱动程序,并迟早告知它设备的配置方式。

它并不总是如此简单,因为卡(或 PCI 的路由表)可能会指定它只能使用某些 IRQ 号,或者 1 MB 的内存必须位于某个地址范围内。PCI 和 ISA 总线的细节有所不同,ISA 总线上的复杂性更高。

一种常用的资源分配方法是从一个设备开始并为其分配总线资源。然后对下一个设备执行相同的操作,依此类推。然后,如果最终所有设备都获得了资源分配而没有冲突,那么一切都正常。但是,如果分配所需的资源会造成冲突,则有必要返回并尝试对先前的分配进行一些更改,以便获得所需的总线资源。这称为重新平衡。Linux 不进行重新平衡,但 MS Windows 在某些情况下会进行重新平衡。对于 Linux,所有这些都由 BIOS 和/或内核和/或设备驱动程序完成。在 Linux 中,设备驱动程序直到驱动程序启动后才获得其最终的资源分配,因此避免冲突的一种方法是不启动任何可能引起冲突的设备。但是,BIOS 通常会在 Linux 甚至启动之前为物理设备分配资源,并且内核会在启动时检查 PCI 设备是否存在地址冲突。

PnP 软件可以使用一些快捷方式。一种是跟踪它上次配置时(上次使用计算机时)如何分配总线资源,并重用此资源。BIOS、MS Windows 和其他系统都这样做,但标准 Linux 不这样做。但在某种程度上,它确实这样做了,因为它通常使用 BIOS 所做的事情。Windows 将此信息存储在其硬盘上的“注册表”中,而 PnP/PCI BIOS 将其存储在 PC 的非易失性内存中(称为 ESCD;请参阅 BIOS 的 ESCD 数据库)。有些人说不拥有注册表(如 Linux)更好,因为对于 Windows,注册表可能会损坏并且难以编辑。但是 Linux 中的 PnP 也存在问题。

虽然 MS Windows(Windows 3.x 和 NT4 除外)是 PnP,但 Linux 最初不是 PnP 操作系统,但已逐渐成为 PnP 操作系统。PnP 最初在 Linux 上工作,是因为 PnP BIOS 会配置总线资源,而设备驱动程序会找出(使用 Linux 内核提供的程序)BIOS 所做的事情。如今,大多数驱动程序都可以发出命令来执行自己的总线资源配置,而无需始终依赖 BIOS。不幸的是,驱动程序可能会抢占另一个设备稍后需要的总线资源。某些设备驱动程序可能会将他们上次使用的配置存储在配置文件中,并在下次计算机开机时使用它。

如果设备硬件记住了其先前的配置,那么在下次启动时就不会有任何硬件需要 PnP 配置。但是,当电源关闭时,硬件似乎会忘记其配置。某些设备包含默认配置(但不一定是上次使用的配置)。因此,每次 PC 开机时都需要重新配置 PnP 设备。此外,如果添加了新设备,那么也需要配置它。为这个新设备分配总线资源可能涉及从现有设备中移除一些总线资源,并为现有设备分配它可以使用的替代总线资源。目前,Linux 无法进行如此复杂的分配(MS Windows XP 也可能无法做到这一点)。

2.13 启动 PC

当 PC 首次开机时,BIOS 芯片运行其程序以启动计算机(第一步是检查主板硬件)。如果操作系统存储在硬盘驱动器上(通常情况下),则 BIOS 必须知道硬盘驱动器。如果硬盘驱动器是 PnP,则 BIOS 可以使用 PnP 方法来查找它。此外,为了允许用户手动配置 BIOS 的 CMOS 并在计算机启动时响应错误消息,还需要屏幕(显卡)和键盘。因此,BIOS 必须始终 PnP 配置从硬盘驱动器加载操作系统所需的设备。

一旦 BIOS 识别出硬盘驱动器、显卡和键盘,它就可以开始启动(从硬盘将操作系统加载到内存中)。如果您已告知 BIOS 您拥有 PnP 操作系统 (PnP OS),则它应按上述方式开始启动 PC,并让操作系统完成 PnP 配置。否则,PnP-BIOS 将(在启动之前)可能尝试完成设备的其余 PnP 配置(但不会告知设备驱动程序它所做的事情)。但是驱动程序仍然可以通过利用 Linux 内核中可用的功能来找出这一点。

2.14 总线

要查看 PCI 总线上有什么,请键入 lspcilspci -vv。或者键入 scanpci -v 以获得相同的信息,但采用数字代码格式,其中设备以数字显示(例如:“设备 0x122d”而不是名称等。在极少数情况下,scanpci 会找到 lspci 找不到的设备。

显示器上的启动时消息显示了在各种总线上找到的设备(使用 Shift+PageUp 向上翻页查看它们)。请参阅 启动时消息

ISA 是老式 IBM 兼容 PC 的旧总线,而 PCI 是英特尔推出的一种更新、更快的总线。PCI 总线被设计用于今天所谓的 PnP(即插即用)。这使得确定 PnP 总线资源如何分配给硬件设备变得容易(与 ISA 总线相比)。

对于 ISA 总线,实现 PnP 确实存在问题,因为在设计 ISA 总线时,没有人考虑到 PnP,并且几乎没有可用的 I/O 地址供 PnP 用于向物理设备发送配置信息。因此,将 PnP 硬塞到 ISA 总线上的方式非常复杂。关于这方面的书已经写了很多。请参阅 PnP 书籍。其中,它要求每个 PnP 设备都由 PnP 程序分配一个临时的“句柄”,以便可以寻址它进行 PnP 配置。分配这些“句柄”称为“隔离”。有关复杂的详细信息,请参阅 ISA 隔离

随着 ISA 总线逐渐淘汰,PnP 将会变得容易一些。那时,不仅更容易了解 BIOS 如何配置硬件,而且冲突也会减少,因为 PCI 可以共享中断。仍然需要将设备驱动程序与设备匹配,还需要配置在 PC 启动并运行后添加的设备。Linux 不支持某些设备的严重问题仍然存在。

2.15 Linux 如何实现 PnP

Linux 过去在处理 PnP 方面存在严重问题,但大多数问题现在已经解决(截至 2004 年中期)。Linux 最初是一个非 PnP 系统,后来发展成为一个可以选择 PnP 的系统,前提是在编译内核时选择了某些选项。BIOS 可能会分配 IRQ,但 Linux 也可能分配其中一些,甚至重新分配 BIOS 所做的分配。ACPI(高级配置和电源接口)的配置部分旨在使操作系统能够轻松地进行自己的配置。如果编译内核时选择了 ACPI,Linux 可以使用它。

在 Linux 中,传统上每个设备驱动程序都进行自己的低级配置。在 Linux 提供内核中的软件供驱动程序使用以简化操作之前,这很困难。今天(2005 年),情况已经发展到驱动程序只需调用内核函数:pci_enable_device(),设备就会被配置,通过启用设备并为其分配 irq(如果需要)和地址。此分配可能是先前由 BIOS 分配的,或者是内核在检测到 pci 或 isapnp 设备时先前为该设备保留的。甚至还有一个 ACPI 选项供 Linux 在启动时分配所有设备的 IRQ。

因此,今天在某种意义上,驱动程序仍然在进行配置,但他们可以通过简单地告诉 Linux 来完成(而 Linux 可能不需要做太多,因为它有时能够使用 BIOS 或 Linux 已经设置的内容)。因此,真正进行大部分配置的是 Linux 内核的非设备驱动程序部分。因此,将 Linux 称为 PnP 操作系统可能是正确的,至少对于常见的计算机架构而言是这样。

然后,当设备驱动程序找到其设备时,它会询问已分配的地址和 IRQ(由 BIOS 和/或 Linux 分配),并且通常只是接受它们。但是,如果驱动程序想要这样做,它可以尝试更改地址,使用内核提供的函数。但是,内核不会接受与其他设备冲突的地址或硬件不支持的地址。当 PC 启动时,您可能会注意到屏幕上显示一些 Linux 设备驱动程序已找到其硬件设备以及 IRQ 和地址范围的消息。

因此,内核为驱动程序提供了函数(程序代码),驱动程序可以使用这些函数来查找其设备是否存在、如何配置,以及修改配置的函数(如果需要)。内核 2.2 只能为 PCI 总线执行此操作,但内核 2.4 具有针对 ISA 和 PCI 总线的此功能(前提是在编译内核时选择了适当的 PNP 和 PCI 选项)。内核 2.6 在 ACPI 的利用方面做得更好。但这绝不保证所有驱动程序都会充分且正确地使用这些功能。并且 BIOS 不知道的遗留设备可能要等到您(或某些配置实用程序)将其地址、irq 等放入配置文件后才能配置。

此外,内核通过不允许它知道的两个设备同时使用相同的总线资源来帮助避免资源冲突。最初这仅适用于 IRQ 和 DMA,但现在也适用于地址资源。

如果您有旧的 ISA 总线,则程序 isapnp 应在启动时运行,以查找和配置 ISA 总线上的 pnp 设备。查看带有“dmesg”的消息。

要查看内核可能为设备驱动程序提供的帮助,请查看目录 /usr/.../.../Documentation,其中一个 ... 包含“kernel-doc”或类似的词。警告:此处的文档往往已过时,因此要获取最新信息,您需要阅读内核开发人员发送的邮件列表中的消息,以及他们编写的计算机代码(包括注释)。在此内核文档目录中,请参阅 pci.txt(“如何编写 Linux PCI 驱动程序”)和文件:/usr/include/linux/pci.h。除非您是驱动程序专家并且了解 C 编程,否则这些文件写得非常简洁,实际上无法让您编写驱动程序。但这会给您一些关于驱动程序可用的 PnP 类型函数的想法。

对于内核 2.4,请参阅 isapnp.txt。对于内核 2.6,isapnp.txt 被 pnp.txt 取代,pnp.txt 与 isapnp.txt 完全不同,并且还涉及 PCI 总线。另请参阅 O'Reilly 的书:《Linux 设备驱动程序》,第 3 版,2005 年。完整文本可在互联网上找到。

2.16 Linux PnP 的问题

但是,真正的 PnP 操作系统可以更好地处理许多事情

由于每个驱动程序都是独立的,因此驱动程序可能会占用其他设备需要的总线资源(但尚未由内核分配给它们)。因此,更完善的 PnP Linux 内核会更好,内核在收到所有请求后进行分配。另一种选择是尝试重新分配已分配的资源,如果设备无法获得其请求的资源。

“总线资源短缺”问题正在变得越来越不严重,原因有两个:一个原因是 PCI 总线正在取代 ISA 总线。在 PCI 下,IRQ 不会短缺,因为 IRQ 可以共享(即使共享效率稍低)。此外,PCI 不使用 DMA 资源(尽管它执行类似于 DMA 的操作,而无需此类资源)。

第二个原因是设备 I/O 可用的地址空间更多。虽然 ISA 总线的传统 I/O 地址空间限制为 64KB,但 PCI 总线有 4GB。由于更多物理设备正在使用主内存地址而不是 IO 地址空间,因此即使在 ISA 总线上,仍有更多可用空间。在 32 位 PC 上,有 4GB 的主内存地址空间,并且大部分总线资源可用于设备 IO(除非您安装了 4GB 的主内存)。

至少有一个早期的尝试使 Linux 成为真正的 PnP 操作系统。请参阅 http://www.astarte.free-online.co.uk。虽然在 1998 年左右开发,但它从未被放入内核(但可能应该放入)。


下一页 上一页 目录