《现代操作系统》-读书笔记-第五章-输入输出

《现代操作系统》-读书笔记-第五章-输入输出

除了提供抽象(例如,进程、地址空间和文件)以外,操作系统还要控制计算机的所有 IO (输入/输出)设备。操作系统必须向设备发送命令,捕捉中断,并处理设备的各种错误。它还应该在设备和系统的其他部分之间提供简单且易于使用的接口。如果有可能,这个接口对于所有设备都应该是相同的,这就是所谓的设备无关性。I/O部分的代码是整个操作系统的重要组成部分。

I/O 硬件原理

I/O 设备

I/O设备大致可以分为两类:块设备(block device)和字符设备(character device)。

块设备把信息存储在固定大小的块中,每个块有自己的地址。通常块的大小在 512 字节至 65536 字节之间。所有传输以一个或多个完整的(连续的)块为单位。块设备的基本特征是每个块都能独立于其他块而读写。硬盘、蓝光光盘和 USB 盘是最常见的块设备。

另一类I/O设备是字符设备。字符设备以字符为单位发送或接收一个字符流,而不考虑任何块结构。字符设备是不可寻址的,也没有任何寻道操作。打印机、网络接口、鼠标(用作指点设备)、以及大多数与磁盘不同的设备都可以看作是字符设备。

这种分类方法并不完美,有些设备就没有包括进去。例如,时钟既不是块可寻址的,也不产生或接收字符流。它所做的工作就是按照预先规定好的时间间隔产生中断。内存映射的显示器也不适用于此模型。但是,块设备和字符设备的模型具有足够的一般性,可以用作使处理I/O设备的某些操作系统软件具有设备无关性的基础。例如,文件系统只处理抽象的块设备,而把与设备相关的部分留给较低层的软件。

I/O设备在速度上覆盖了巨大的范围,要使软件在跨越这么多数量级的数据率下保证性能优良,给软件造成了相当大的压力。图 5-1 列出了某些常见设备的数据率,这些设备中大多数随着时间的推移而变得越来越快。

设备控制器

I/O设备一般由机械部件和电子部件两部分组成。通常可以将这两部分分开处理,以提供更加模块化和更加通用的设计。电子部件称作设备控制器(device controller)或适配器(adapter)。在个人计算机上,它经常以主板上的芯片的形式出现,或者以插入(PCI)扩展槽中的印刷电路板的形式出现。机械部件则是设备本身。

控制器的任务是把串行的位流转换为字节块,并进行必要的错误校正工作。字节块通常首先在控制器内部的一个缓冲区中按位进行组装,然后再对校验和进行校验并证明字节块没有错误后,再将它复制到主存中。

内存映射I/O

每个控制器有几个寄存器用来与 CPU 进行通信。通过写入这些寄存器,操作系统可以命令设备发送数据、接收数据、开启或关闭,或者执行某些其他操作。通过读取这些寄存器,操作系统可以了解设备的状态,是否谁备好接收一个新的命令等。

除了这些控制寄存器以外,许多设备还有一个操作系统可以读写的数据缓冲区。例如,在屏幕上显示像素的常规方法是使用一个视频 RAM,这一RAM 基本上只是一个数据缓冲区,可供程序或操作系统写人数据。

于是,问题就出现了:CPU如何与设备的控制寄存器和数据缓冲区进行通信?

存在两个可选的方法。 在第一个方法中,每个控制寄存器被分配一个I/O端口(I/O port)号,这是一个 8 位或 16 位的整数。所 有I/O端口形成I/O端口空间(I/O port space),并且受到保护使得普通的用户程序不能对其进行访问 (只有操作系统可以访问)。使用一条特殊的I/O 指令,例如

IN REG, PORT

CPU 可以读取控制寄存器 PORT 的内容并将结果存人到 CPU 寄存器 REG 中。

第二个方法是 PDP-11 引入的,它将所有控制寄存器映射到内存空间中,如图 5-2 b 所示。每个控制寄存器被分配唯一的一个内存地址,并且不会有内存被分配这一地址。这样的系统称为内存映射 I/O (memory -mapped I/O)。在大多数系统中,分配给控制寄存器的地址位于或者靠近地址空间的顶端。图 5-2c所示是一种混合的方案,这一方案具有内存映射I/O的数据缓冲区,而控制寄存器则具有单独的I/O 端口。x86 采用这一体系结构。在 IBM PC 兼容机中,除了 0 到 64K – 1的I/O端口之外,640K 到1M – 1 的内存地址保留给设备的数据缓冲区。

两种方法的优缺点,

对于内存映射I/O,其优点为:

  • I/O设备驱动程序可以完全用C语言编写。如果不使用内存映射I/O,就要用到某些汇编代码。
  • 对于内存映射I/O,不需要特殊的保护机制来阻止用户进程执行I/O操作。
  • 对于内存映射I/O,可以引用内存的每一条指令也可以引用控制寄存器。

缺点是:

现今大多数计算机都拥有某种形式的内存字高速缓存。对一个设备控制寄存器进行高速缓存可能是灾难性的。在存在高速缓存的情况下考虑上面给出的汇编代码循环。第一次引用 PORT_ 4 将导致它被高速缓存,随后的引用将只从高速缓存中取值并且不会再查询设备。之后当设备最终变为就绪时,软件将没有办法发现这一点。结果,循环将永远进行下去。对内存映射I/O,为了避免这一情形,硬件必须能够针对每个页面有选择性地禁用高速缓存。操作系统必须管理选择性高速缓存,所以这一特性为硬件和操作系统两者增添了额外的复杂性。

直接存储器存取

无论一个CPU是否具有内存映射I/O,它都需要寻址设备控制器以便与它们交换数据。CPU 可以从 I/O控制器每次请求一个字节的数据,但是这样做浪费CPU的时间,所以经常用到一种称为直接存储器存取(Direct Memory Access, DMA)的不同方案。

只有硬件具有 DMA 控制器时操作系统才能使用 DMA,而大多数系统都有 DMA 控制器。有时 DMA 控制器集成到磁盘控制器和其他控制器之中,但是这样的设计要求每个设备有一个单独的 DMA 控制器。更加普遍的是,只有一个 DMA 控制器可利用(例如,在主板上),由它调控到多个设备的数据传送,而这些数据传送经常是同时发生的。

无论 DMA 控制器在物理上处于什么地方,它都能够独立于 CPU 而访问系统总线,如图 5-4 所示。它包含若千个可以被 CPU 读写的寄存器,其中包括一个内存地址寄存器、一个字节计数寄存器和一个或多 个控制寄存器。控制寄存器指定要使用的I/O端口、传送方向(从IO设备读或写到I/O 设备)、传送单位(每次一个字节或每次一个字)以及在一次突发传送中要传送的字节数。

重温中断

在一台典型的个人计算机系统中, 中断结构如图5-5所示。在硬件层面,中断的工作如下所述。当一个I/O设备完成交给它的工作时,它就产生一个中断(假设操作系统已经开放中断),它是通过在分配给它的一条总线信号线上置起信号而产生中断的。该信号被主板上的中断控制器芯片检测到,由中断控制器芯片决定做什么。

如果没有其他中断悬而未决,中断控制器将立刻对中断进行处理。如果有另一个中断正在处理中,或者另一个设备在总线上具有更高优先级的一条中断请求线上同时发出中断请求,该设备将暂时不被理睬。在这种情况下,该设备将继续在总线上置起中断信号,直到得到 CPU 的服务。

为了处理中断,中断控制器在地址线上放置一个数字表明哪个设备需要关注,并且置起一个中断 CPU 的信号。

中断信号导致 CPU 停止当前正在做的工作并且开始做其他的事情。地址线上的数字被用做指向一个称为中断向量(interrupt vector)的表格的索引,以便读取一个新的程序计数器。这一程序计数器指向相应的中断服务过程的开始。一般情况下,陷阱和中断从这一点上看使用相同的机制,并且常常共享相同的中断向量。中断向量的位置可以硬布线到机器中,也可以在内存中的任何地方通过一个 CPU 寄存器(由操作系统装载)指向其起点。

I/O 软件原理

I/O软件的目标

在设计I/O软件时一个关键的概念是设备独立性(device independence)。它的意思是应该能够编写出这样的程序: 它可以访问任意I/O设备而无需事先指定设备。例如,读取一个文件作为输入的程序应该能够在硬盘、DVD 或者 USB 盘上读取文件,无需为每一种不同的设备修改程序。类似地,用户应该能够键入这样一条命令:

sort  <input> output

与设备独立性密切相关的是统一命名(uniform naming)这一目标。一个文件或一个设备的名字应该是一个简单的字符串或一个整数,它不应依赖于设备。在 UNIX 系统中,所有存储盘都能以任意方式集成到文件系统层次结构中,因此,用户不必知道哪个名字对应于哪台设备。例如,一个 USB 盘可以安装 ( mount)到目录/usr/ ast/backup下,这样复制一个文件到/usr/ ast/backup/ monday 就是将文件复制到 USB 盘上。用这种方法,所有文件和设备都采用相同的方式一路径名进行寻址。

I/O软件的另一个重要问题是错误处理(error handling)。一般来说,错误应该尽可能地在接近硬件的层面得到处理。当控制器发现了一个读错误时,如果它能够处理那么就应该自己设法纠正这一错误。如果控制器处理不了,那么设备駆动程序应当予以处理,可能只需重读一次这块数据就正确了。很多错误是偶然性的,例如,磁盘读写头上的灰尘导致读写错误时,重复该操作,错误经常就会消失。只有在低层软件处理不了的情况下,才将错误上交高层处理。在许多情况下,错误恢复可以在低层透明地得到解决,而高层软件甚至不知道存在这一错误。

另一个关键问题是同步(synchronous;即阻塞)和异步(asynchronous; 即中断驱动) 传输。大多数物理I/O是异步的CPU启动传输后便转去做其他工作,直到中断发生。如果 I/O 操作是阻塞的,那么用户程序就更加容易编写一在 read 系统调用之后,程序将自动被挂起,直到缓冲区中的数据准备好。正是操作系统使实际上是中断驱动的操作变为在用户程序看来是阻塞式的操作。然而,某些性能极高的应用程序需要控制 IO 的所有细节,所以某些操作系统使异步IO 对这样的应用程序是可用的。

I/O 软件的另一个问题是缓冲(buffering)。数据离开一个设备之后通常并不能直接存放到其最终的目的地。例如,从网络上进来一个数据包时,直到将该数据包存放在某个地方并对其进行检查,操作系统才知道要将其置于何处。此外,某些设备具有严格的实时约束(例如,数字音频设备),所以数据必须预先放置到输出缓冲区之中,从而消除缓冲区填满速率和缓冲区清空速率之间的相互影响, 以避免缓 冲区欠载。缓冲涉及大量的复制工作,并且经常对I/O性能有重大影响。

此处我们将提到的最后一个概念是共享设备和独占设备的问题。有些 I/O 设备(如磁盘)能够同时让多个用户使用。多个用户同时在同一磁盘上打开文件不会引起什么问题。其他设备(如磁带机)则必须由单个用户独占使用,直到该用户使用完,另一个用户才能拥有该磁带机。让两个或更多的用户随机地将交又混杂的数据块写入相同的磁带是注定不能工作的。独占(非共享)设备的引入也带来了各种各样的问题,如死锁。同样,操作系统必须能够处理共享设备和独占设备以避免问题发生。

程序控制I/O

I/O可以采用三种根本上不同的方式来实现:

  • 程序控制I/O
  • 中断驱动I/O
  • DMA的I/O

I/O的最简单形式是让CPU做全部工作,这一方法称为程序控制 I/O (programmed I/O)。

借助于例子来说明程序控制I/O是最简单的。考虑一个用户进程,该进程想通过串行接口在打印机上打印 8 个字符的字符串“ABCDEFGH”。在某些嵌入式系统上显示有时就是这样工作的。

首先,数据被复制到内核空间。然后,操作系统 进入一个密闭的循环,一次输出一个字符。CPU 要不断地查询设备以了解它是否就绪准备接收另一个字符。这一行为经常称为轮询(polling)或忙等待(busy waiting)。

程序控制I/O十分简单但是有缺点,即直到全部I/O完成之前要占用CPU的全部时间。如果a“打印”一个字符的时间非常短(因为打印机所做的全部事情就是将新的字符复制到一个内部缓冲区中),那么忙等待还是不错的。此外,在嵌人式系统中,CPU 没有其他事情要做,忙等待也是合理的。然而,在更加复杂的系统中,CPU有其他工作要做,忙等待将是低效的,需要更好的I/O方法。

中断控制I/O

现在我们考虑在不缓冲字符而是在每个字符到来时便打印的打印机上进行打印的情形。如果打印机每秒可以打印 100 个字符,那么打印每个字符将花费 10 ms。这意味着,当每个字符写到打印机的数据寄存器中之后,CPU 将有 10 ms 搁置在无价值的循环中,等待允许输出下一个字符。这 10 ms 时间足以进行一次上下文切换并且运行其他进程,否则就浪费了。

这种允许 CPU 在等待打印机变为就绪的同时做某些其他事情的方式就是使用中断。当打印字符串的系统调用被发出时,如我们前面所介绍的,字符串缓冲区被复制到内核空间,并且一旦打印机谁备好接收一个字符时就将第一个字符复制到打印机中。这时,CPU 要调用调度程序,并且某个其他进程将运行。请求打印字符串的进程将被阻塞,直到整个字符串打印完。当打印机将字符打印完并且准备好接收下一个字符时,它将产生一个中断。这个中断将停止当前进程并且保存其状态。然后,打印机中断服务过程将运行。如果没有更多的字符要打印,中断处理程序将采取某个操作将用户进程解除阻塞。否则,它将输出下一个字符,应答中断,并且返回中断之前正在运行的进程,该进程将从其他的地方继续运行。

使用DMA的I/O

中断驱动I/O的一个明显缺点是中断发生在每个字符上。中断要花费时间,所以这一方法将浪费一定数量的 CPU 时间。这一问题的一种解决方法是使用 DMA。此处的思路是让 DMA 控制器一次给打印机提供一个字符,而不必打扰CPU。本质上,DMA控制器而不是主CPU做全部工作。这一策略需要特殊的硬件(DMA控制器),但是使CPU获得自由从而可以在I/O期间做其他工作。

DMA重大的成功是将中断的次数从打印每个字符一次减少到打印每个缓冲区一次。如果有许多字符并且中断十分缓慢,那么采用DMA可能是重要的改进。另一个方面,DMA 控制器通常比主 CPU 要慢很多。如果 DMA 控制器不能以全速驱动设 备,或者CPU在等待DMA中断的同时没有其他事情要做,那么采用中断驱动I/O甚至采用程序控制I/O 也许更好。

I/O软件层次

I/O软件通常组织成四个层次。每一层具有一个要执行的定义明确的功能和一个定义明确的与邻近层次的接口。功能与接口随系统的不同而不同。

中断处理程序

虽然程序控制I/O偶尔是有益的,但是硬件对于大多数I/O而言,中断是令人不愉快的事情并且无法避免。应当将其深深地隐藏在操作系统内部,以便系统的其他部分尽量不与它发生联系。隐藏它们的最好办法是将启动一个I/O操 作的驱动程序阻塞起来,直到I/O操作完成并且产生一个中断。驱动程序可以阻塞自己,例如,在一个信号量上执行 down 操作、在一个条件变量上执行 wait 操作、在一个消息上执行 receive 操作或者某些类似的操作。

设备驱动程序

每一个控制器都设有某些设备寄存器用来向设备发出命令,或者设有某些设备寄存器用来读出设备的状态,或者设有这两种设备寄存器。设备寄存器的数量和命令的性质在不同设备之间有着根本性的不同。

每个连接到计算机上的I/O设备都需要某些设备特定的代码来对其进行控制。这样的代码称为设备驱动程序(device driver),它一般由设备的制造商编写并随同设备一起交付。因为每一个操作系统都需要自己的驱动程序,所以设备制造商通常要为若干流行的操作系统提供驱动程序。

每个设备驱动程序通常处理一种类型的设备,或者至多处理一类紧密相关的设备。

不过在有些时候,极其不同的设备却基于相同的底层技术。众所周知的例子可能是 USB,这是一种串行总线技术,称其为“通用”并不是无缘无故的。因为很多设备都使用 USB,但是它们做着非常不同的事情。此处的技巧是 USB 驱动程序通常是堆 栈式的,就像是网络中的TCP/IP栈。在底层,特别是在硬件中,我们会发现USB链路层(串行I/O),这一层处理硬件事物,例如发信号以及将信号流译码成 USB 包。这一层被较高的层次所使用,而这些较高的层次则处理数据包以及被大多数设备所共享的 USB 通用功能。最后,在顶层我们会发现高层 API,例如针对大容量存储设备和照相机等的接口。因此,我们依然拥有分开的设备驱动程序,尽管它们共享部分协议栈。

为了访问设备的硬件(意味着访问设备控制器的寄存器),设备驱动程序通常必须是操作系统内核的一部分,至少对目前的体系结构是如此。实际上,有可能构造运行在用户空间的驱动程序,使用系统调用来读写设备寄存器。

与设备无关的I/O软件

虽然I/O软件中有一些是设备特定的,但是其他部分I/O软件是与设备无关的。设备驱动程序和与设备无关的软件之间的确切界限依赖于具体系统(和设备),因为对于一些本来应按照与设备无关方式实现的功能,出于效率和其他原因,实际上是由驱动程序来实现的。图 5-13 所示的功能典型地由与设备无关的软件实现。我洗与设备无关的软件的基本功能是执行对所有设备公共的I/O功能,并且向用户层软件提供一个统一的接口。

用户空间的I/O软件

尽管大部分I/O软件都在操作系统内部,但是仍然有一小部分在用户空间,包括与用户程序连接在一起的库,甚至完全运行于内核之外的程序。系统调用(包括I/O系统调用)通常由库过程实现。当一个 C 程序包含调用

count=write (fd, buffer, nbytes);

时,库过程 write 将与该程序连接在一起,并包含在运行时出现在内存中的二进制程序中。所有这些库过 程的集合显然是I/O系统的组成部分。

虽然这些过程所做的工作不过是将这些参数放在合适的位置供系统调用使用,但是确有其他I/O 过程实际实现真正的操作。

图5-17对I/O系统进行了总结,给出了所有层次以及每一层的主要功能。从底部开始,这些层是硬件、中断处理程序、设备驱动程序、与设备无关的软件,最后是用户进程。

图 5-17 中的箭头表明了控制流。例如,当一个用户程序试图从一个文件中读一个块时,操作系统被调用以实现这一请求与设备无关的软件在缓冲区高速缓存中査找有无要读的块。如果需要的块不在其中,则调用设备驱动程序,向硬件发出一个请求,让它从磁盘中获取该块。然后,进程被阻塞直到磁盘操作完成并且数据在调用者的缓冲区中安全可用。

当磁盘操作完成时,硬件产生一个中断。中断处理程序就会运行,它要查明发生了什么事情,也就是说此刻需要关注哪个设备。然后,中断处理程序从设备提取状态信息,唤醒休眠的进程以结束此次I/O请求,并且让用户进程继续运行。

盘硬件

盘具有多种多样的类型。最为常用的是磁盘,它们具有读写速度同样快的特点,这使得它们适合作为辅助存储器(用于分页、文件系统等)。这些盘的阵列有时用来提供高可靠性的存储器。对于程序、数据和电影的发行而言,光盘(DVD 和蓝光光盘)也非常重要。最后,固态盘越来越流行,它们速度快并且不包含运动的部件。

磁盘

磁盘被组织成柱面,每一个柱面包含若干磁道,磁道数与垂直堆叠的磁头个数相同。磁道又被分成若干扇区,软盘上大约每条磁道有 8~32 个扇区,硬盘上每条磁道上扇区的数目可以多达几百个。磁头数大约是 1~16 个。

老式的磁盘只有少量的电子设备,它们只是传送简单的串行位流。在这些磁盘上,控制器做了大部分的工作。在其他磁盘上,特别是在 IDE (Integrated Drive Electronics,集成驱动电子设备)和 SATA (Serial ATA,串行 ATA)盘上,磁盘驱动器本身包含一个微控制器,该微控制器承担了大量的工作并且允许实际的控制器发出一组高级命令。控制器经常做磁道高速缓存、坏块重映射以及更多的工作。

对磁盘驱动程序有重要意义的一个设备特性是:控制器是否可以同时控制两个或多个驱动器进行寻道,这就是重叠寻道(overlapped seek)。当控制器和软件等待一个驱动器完成寻道时,控制器可以同时启动另一个驱动器进行寻道。许多控制器也可以在一个驱动器上进行读写操作,与此同时再对另一个或多个其他驱动器进行寻道,但是软盘控制器不能在两个驱动器上同时进行读写操作。(读写数据要求控制器在微秒级时间尺度传输数据,所以次传输就用完了控制器大部分的计算能力。)对于具有集成控制器的硬盘而言情况就不同了,在具有一个以上这种硬盘驱动器的系统上,它们能够同时操作,至少在磁盘与控制器的缓冲存储器之间进行数据传输的限度之内是这样。然而,在控制器与主存之间可能同时只有一次传输。同时执行两个或多个操作的能力极大地降低了平均存取时间。

为了隐藏每个磁道有多少扇区的细节,大多数现代磁盘都有一个虚拟几何规格呈现给操作系统。软件在工作时仿佛存在着 x 个柱面、y 个磁头、每磁道 z 个扇区,而控制器则将对(x, y, z)的请求重映射到实际的柱面、磁头和扇区。对于图 5-19 a 中的物理磁盘,一种可能的虚拟几何规格如图 5- 19 b 所示。在两种情形中磁盘拥有的扇区数都是 192,只不过公布的排列与实际的排列是不同的。

对于 PC 而言,上述三个参数的最大值常常是(65535,16,63),这是因为需要与最初 IBM PC 的限制向后兼容。在 IBM PC 器上,使用 16 位、4 位和 6 位的字段来设定这些参数,其中柱面和扇区从 1 开始编号,磁头从 0 开始编号。根据这些参数以及每个扇区 512 字节可知,磁盘最大可能的容量是 31.5 GB。为突破这一~限制,所有现代磁盘现在都支持一~种称为逻辑块寻址(logical block addressing, LBA)的系统,在这样的系统中,磁盘扇区从 0 开始连续编号,而不管磁盘的几何规格如何。

RAID

Patterson等人在他们1988年写的文章中提出,使用六种特殊的磁盘组织可能会改进磁盘的性能、可靠性或者同时改进这两者(Patterson 等人,1988)。这些思想很快被工 业界所采纳,并且导致称为RAID的一种新型I/O设备的诞生。Patterson 等人将 RAID 定义为 Redundant Array of Inexpensive Disk(廉价磁盘冗余阵列),但是工业界将其重定义为 Independent(独立)而不是 Inexpensive(廉价),或许这样他们就可以收取更多的费用?因为反面角色也是需要的(如同 RISC 对 CISC,这也是源于 Patterson),此处的“坏家伙”是 SLED (Single Large Expensive Disk,单个大容量昂贵磁盘)。

RAID 背后的基本思想是将一个装满了磁盘的盒子安装到计算机(通常是一个大型服务器)上,用 RAID 控制器替换磁盘控制器卡,将数据复制到整个 RAID 上,然后继续常规的操作。换言之,对操作系统而言一个 RAID 应该看起来就像是一个 SLED,但是具有更好的性能和更好的可靠性。由于 SCSI 盘具有良好的性能、较低的价格并且在单个控制器上能够容纳多达 7 个驱动器(对宽型 SCSI 而言是 15 个),很自然地大多数 RAID 由一个 RAID SCSI 控制器加上一个装满了 SCSI 盘的盒子组成,而对操作系统而言这似乎就是一个大容量磁盘。以这样的方法,不需要软件做任何修改就可以使用 RAID,对于许多系统管理员来说这可是一大卖点。

除了对软件而言看起来就像是一个磁盘以外,所有的 RAID 都具有同样的特性,那就是将数据分布在全部驱动器上,这样就可以并行操作。Patterson 等人为这样的操作定义了几种不同的模式。如今,大多数制造商将七种标准配置称为 0 级 RAID 到 6 级 RAID。此外,还有少许其他的辅助层级,我们就不讨论了。“层级”这一术语多少有一些用词不当,因为此处不存在分层结构,它们只是可能的七种不同组织形式而已。

磁盘格式化

在磁盘能够使用之前,每个盘片必须经受由软件完成的低级格式化(low-level format)。该格式包含一系列同心的磁道,每个磁道包含若干数目的扇区,扇区间存在短的间隙。

低级格式化的结果是磁盘容量减少,减少的量取决于前导码、扇区间间隙和 ECC 的大小以及保留的备用扇区的数目。通常格式化的容量比未格式化的容量低 20%。备用扇区不计人格式化的容量,所以一种给定类型的所有磁盘在出厂时具有完全相同的容量,与它们实际具有多少坏扇区无关(如果坏扇区的数目超出了备用扇区的数目,则该驱动器是不合格的,不会出厂)。

在低级格式化完成之后,要对磁盘进行分区。在逻辑上,每个分区就像是一个独立的磁盘。分区对于实现多个操作系统共存是必需的。此外,在某些情况下,分区可以用来进行交换。在 x86 和大多数其他计算机上,0 扇区包含主引导记录(Master Boot Record, MBR),它包含某些引导代码以及处在扇区末尾的分区表。

磁盘臂调度算法

读或者写一个磁盘块需要多长时间。这个时间由以下三个因素决定:

1) 寻道时间(将磁盘臂移动到适当的柱面上所需的时间)

2) 旋转延迟(等待适当扇区旋转到磁头下所需的时间)

3) 实际数据传输时间

对大多数磁盘而言,寻道时间与另外两个时间相比占主导地位,所以减少平均寻道时间可以充分地改善系统性能。

如果磁盘驱动程序每次接收一个请求并按照接收顺序完成请求,即先来先服务(First-Come, First- Served, FCFS),则很难优化寻道时间。然而,当磁盘负载很重时,可以采用其他策略。

另一种方法是下一次总是处理与磁头距离最近的请求以使寻道时间最小化。这个算法即最短寻道优先(Shortest Seek First, SSF),与 FCFS 算法相比,该算法的磁盘臂移动几乎减少了一半。

但是,SSF 算法存在一个问题如果磁盘负载很重,那么大部分时间磁盘臂将停留在磁盘的中部区域,而两端极端区域的请求将不得不等待,直到负载中的统计波动使得中部区域没有请求为止。远离中部区域的请求得到的服务很差。因此获得最小响应时间的目标和公平性之间存在着冲突。

电梯算法:大多数电梯使用一种不同的算法来协调效率和公平性这两个相互冲突的目标。电梯保持按一个方向移动,直到在那个方向上没有请求为止,然后改变方向。这个算法在磁盘世界和电梯世界都被称为电梯算法(elevator algorithm),它需要软件维护一个二进制位,即当前方向位:UP(向上)或是 DOWN(向下)。当一个请求处理完之后,磁盘或电梯的驱动程序检査该位,如果是 UP,磁盘臂或电梯舱移至下一个更高的未完成的请求。如果更高的位置没有未完成的请求,则方向位取反。当方向位设置为 DOWN 时,同时存在一个低位置的请求,则移向该位置。如果不存在未决的请求,那么它只是停止并等待。

错误处理

制造时的瑕疵会引入坏扇区,也就是说,扇区不能正确地读回刚刚写到其上的值。如果瑕疵非常小,比如说只有几位,那么使用坏扇区并且每次只是让 ECC 校正错误是可能的。如果瑕疵较大,那么错误就不可能被掩盖。

对于坏块存在两种一般的处理方法:在控制器中对它们进行处理或者在操作系统中对它们进行处理。在前一种方法中,磁盘在从エ厂出厂之前要进行测试,并且将一个坏扇区列表写在磁盘上。对于每一个坏扇区,用一个备用扇区替换它。

有两种方法进行这样的替换。在图 5-26 a 中,我们看到单个磁盘磁道,它具有 30 个数据扇区和两个备用扇区。扇区 7 是有瑕疵的。控制器所能够做的事情是将备用扇区之一重映射为扇区 7, 如图 5-26 b 所示。另一种方法是将所有扇区向上移动一个扇区,如图 5-26 c 所示。在这两种情况下,控制器都必须知道哪个扇区是哪个扇区。它可以通过内部的表来跟踪这一信息(每个磁道一张表),或者通过重写前导码来给出重映射的扇区号。如果是重写前导码,那么图 5-26 c 的方法就要做更多的工作(因为 23 个前导码必须重写),但是最终会提供更好的性能,因为整个磁道仍然可以在旋转一周中读出。

正如我们已经看到的,控制器实际是一个专用的小计算机,它有软件、变量、缓冲区,偶尔还出现故障。

稳定存储器

对于某些应用而言,决不丢失或破坏数据是绝对必要的,即使面临磁盘和 CPU 错误也是如此。理想的情况是,磁盘应该始终没有错误地工作。但是,这是做不到的。所能够做到的是,一个磁盘子系统具有如下特性:当一个写命令发给它时,磁盘要么正确地写数据,要么什么也不做,让现有的数据完整无缺地留下。这样的系统称为稳定存储器(stable storage),并且是在软件中实现的(Lampson 和 Sturgis, 1979)。目标是不惜一切代价保持磁盘的一致性。下面我们将描述这种最初思想的一个微小的变体。

稳定存储器使用一对完全相同的磁盘,对应的块一同工作以形成一个无差错的块。当不存在错误时在两个驱动器上对应的块是相同的,读取任意一个都可以得到相同的结果。为了达到这一目的,定义了下述三种操作

1) 稳定写(stable write)。稳定写首先将块写到駆动器 1 上,然后将其读回以校验写的是正确的。如果写的不正确,那么就再次做写和重读操作,一直到次,直到正常为止。经过 n 次连续的失败之后,就将该块重映射到一个备用块上,并且重复写和重读操作直到成功为止,无论要尝试多少个备用块。在对驱动器 1 的写成功之后,对駆动器 2 上对应的块进行写和重读,如果需要的话就重复这样的操作,直到最后成功为止。如果不存在 CPU 崩溃,那么当稳定写完成后,块就正确地被写到两个驱动器上,并且在两个駆动器上得到校验

2) 稳定读(stable read)。稳定读首先从驱动器 1 上读取块。如果这一操作产生错误的 ECC,则再次尝试读操作,一直到 n 次。如果所有这些操作都给出错误的 ECC,则从驱动器 2 上读取对应的数据块。给定一个成功的稳定写为数据块留下两个可靠的副本这样的事实,并且我们假设在合理的时间间隔内相同的块在两个驱动器上自发地变坏的概率可以忽略不计,那么稳定读就总是成功的。

3) 崩溃恢复(crash recovery)。崩溃之后,恢复程序扫描两个磁盘,比较对应的块。如果一对块都是好的并且是相同的,就什么都不做。如果其中一个具有 ECC 错误,那么坏块就用对应的好块来覆盖。如果一对块都是好的但是不相同,那么就将驱动器 1 上的块写到驱动器 2 上。

时钟

时钟(clock)又称为定时器(timer),由于各种各样的原因决定了它对于任何多道程序设计系统的操作都是至关重要的。时钟负责维护时间,并且防止一个进程垄断 CPU,此外还有其他的功能。时钟软件可以采用设备驱动程序的形式,尽管时钟既不像磁盘那样是一个块设备,也不像鼠标那样是一个字符设备。

时钟硬件

在计算机里通常使用两种类型的时钟,这两种类型的时钟与人们使用的钟表和手表有相当大的差异。比较简单的时钟被连接到 110 V 或 220 V 的电源线上,这样每个电压周期产生一个中断,频率是 50 Hz 或 60 Hz。这些时钟过去曾经占据统治地位,但是如今却非常罕见。

另一种类型的时钟由三个部件构成:晶体振荡器、计数器和存储寄存器。

石英晶体适当地切割并且安装在一定的电压之下时,它就可以产生非常精确的周期性信号,典型的频率范围是几百兆赫兹,具体的频率值与所选的晶体有关。使用电子器件可以将这一基础信号乘以一个小的整数来获得高达 1000 MHz 甚至更高的频率。在任何一台计算机里通常都可以找到至少一个这样的电路,它给计算机的各种电路提供同步信号。该信号被送到计数器,使其递减计数至 0。当计数器变为 0 时,产生一个 CPU 中断。

可编程时钟通常具有几种操作模式。在一次完成模式(one-shot mode)下,当时钟启动时,它把存储寄存器的值复制到计数器中,然后,来自晶体的每一个脉冲使计数器减 1。当计数器变为 0 时,产生一个中断,并停止工作,直到软件再一次显式地启动它。在方波模式(square -wave mode)下,当计数器变为 0 并且产生中断之后,存储寄存器的值自动复制到计数器中,并且整个过程无限期地再次重复下去。这些周期性的中断称为时钟滴答(clock tick)。

可编程时钟的优点是其中断频率可以由软件控制。如果采用 500 MHz 的晶体,那么计数器将每隔 2 ns 脉动一次。对于(无符号)32 位寄存器,中断可以被编程为从 2 ns 时间间隔发生一次到 8.6 s 时间间隔发生一次。可编程时钟芯片通常包含两个或三个独立的可编程时钟,并且还具有许多其他选项(例如,用正计时代替倒计时、屏蔽中断等)。

为了防止计算机的电源被切断时丢失当前时间,大多数计算机具有一个由电池供电的备份时钟,它是由在数字手表中使用的那种类型的低功耗电路实现的。电池时钟可以在系统启动的时候读出。如果不存在备份时钟,软件可能会向用户询问当前日期和时间。对于一个连人网络的系统而言还有一种从远程主机获取当前时间的标准方法。无论是哪种情况,当前时间都要像 UNIX 所做的那样转换成自 1970 年 1 月 1 日上午 12 时 UTC (Universal Time Coordinated,协调世界时,以前称为格林威治平均时)以来的时钟滴答数,或者转换成自某个其他标准时间以来的时钟滴答数。Windows 的时间原点是 1980 年 1 月 1 日。每一次时钟滴答都使实际时间增加个计数。通常会提供实用程序来手工设置系统时钟和备份时钟,并且使两个时钟保持同步。

时钟软件

时钟硬件所做的全部工作是根据已知的时间间隔产生中断。涉及时间的其他所有工作都必须由软件一时钟驱动程序完成。时钟驱动程序的确切任务因操作系统而异,但通常包括下面的大多数任务:

1) 维护日时间。

2) 防止进程超时运行。(每当启动一个进程时,调度程序就将一个计数器初始化为时钟滴答为单位的该进程时间片取值。每次时钟)

3) 对 CPU 的使用情况记账。

4) 处理用户进程提出的 alarm 系统调用。

5) 为系统本身各个部分提供监视定时器。

6) 完成概要剖析、监视和统计信息收集。

时钟的第一个功能是维持正确的日时间,也称为实际时间(real time),这并不难实现,只需要如前面提到的那样在每个时钟滴答将计数器加 1 即可。唯一-要当心的事情是日时间计数器的位数,对于一个频率为 60 Hz 的时钟来说,32 位的计数器仅仅超过 2 年就会溢出。很显然,系统不可能在 32 位寄存器中按照自 1970 年 1 月 1 日以来的时钟滴答数来保存实际时间。

可以采取三种方法来解决这一问题。第一种方法是使用一个 64 位的计数器,但这样做会使维护计数器的代价变得很高,因为 1 秒内需要做很多次维护计数器的工作。第二种方法是以秒为单位维护日时间,而不是以时钟滴答为单位,该方法使用一个辅助计数器来对时钟滴答计数,直到累计至完整的一秒。因为 232 秒超过了 136 年,所以该方法可以工作到 22 世纪。

第三种方法是对时钟滴答计数,但是这一计数工作是相对于系统引导的时间,而不是相对于一个固定的外部时间。当读入备份时钟或者用户输人实际时间时,系统引导时间就从当前日时间开始计算,并且以任何方便的形式存放在内存中。以后,当请求日时间时,将存储的日时间值加到计数器上就可以得到当前的日时间。所有这三种方法如图 5-29 所示。

软定时器

大多数计算机拥有辅助可编程时钟,可以设置它以程序需要的任何速率引发定时器中断。该定时器是主系统定时器以外的,而主系统定时器的功能已经在上面讲述了。只要中断频率比较低,将这个辅助定时器用于应用程序特定的目的就不存在任何问题。但是当应用程序特定的定时器的频率非常高时,麻烦就来了。下面我们将简要描述一个基于软件的定时器模式,它在许多情况下性能良好,甚至在相当高的频率下也是如此。这一思想起因于(Aron 和 Druschel, 1999)。关于更详细的细节,请参阅他们的论文。

一般而言,有两种方法管理I/O:中断和轮询。中断具有较低的响应时间(latency),也就是说,中断在事件本身之后立即发生,具有很少的延迟(delay)或者没有延迟。另一方面,对于现代 CPU 而言,由于需要上下文切换以及对于流水线、TLB 和高速缓存的影响,中断具有相当大的开销。

替代中断的是让应用程序对它本身期待的事件进行轮询。这样做避免了中断,但是可能存在相当长的等待时间,因为一个事件可能正好发生在一次轮询之后,在这种情况下它就要等待几乎整个轮询间隔。平均而言,等待时间时轮询间隔的一半。

对于某些应用而言,中断的开销和轮询的等待时间都是不能接受的。

软定时器(soft timer)避免了中断。无论何时当内核因某种其他原因在运行时,在它返回到用户态之前,它都要检查实时时钟以了解软定时器是否到期。如果这个定时器已经到期,则执行被调度的事件(如,传送数据包或者检查到来的数据包),而无需切换到内核态,因为系统已经在内核态。在完成工作之后,软定时器被复位以便再次闹响。要做的全部工作是将当前时钟值复制给定时器并且将超时间隔加上。

软定时器随着因为其他原因进入内核的频率而脉动,这些原因包括:

1) 系统调用

2) TLB 未命中

3) 页面故障

4) I/O中断

5) CPU 变成空闲

用户界面:键盘、鼠标和监视器

每台通用计算机都配有一个键盘和一个监视器(并且通常还有一只鼠标),使人们可以与之交互。尽管键盘和监视器在技术上是独立的设备,但是它们紧密地一同工作。在大型机上,通常存在许多远程用户,每个用户拥有一个设备,该设备包括一个键盘和一个连在一起的显示器作为一个单位。这些设备在历史上被称为终端(terminal)。

输入软件

用户输入主要来自键盘和鼠标(有时还有触摸屏),所以我们要了解它们。在个人计算机上,键盘包含一个嵌人式微处理器,该微处理器通过一个特殊的串行端口与主板上的控制芯片通信(尽管键盘越来越多地连接到 USB 端口上)。每当一个键被按下的时候都会产生一个中断,并且每当一个键被释放的时候 还会产生第二个中断。每当发生这样的键盘中断时,键盘驱动程序都要从与键盘相关联的I/O端口提取信息,以了解发生了什么事情。其他的一一切事情都是在软件中发生的,在相当大的程度上独立于硬件。

键盘软件

I/O端口中的数字是键的编号,称为扫描码 (scan code),而不是 ASCII 码。键盘所拥有的键不超过 128 个,所以只需 7 个位表示键的编号。当键按下时,第 8 位设置为 0,当键释放时,第 8 位设置为 1。跟踪每个键的状态(按下或弹起)是驱动程序的任务。所以,硬件所做的全部工作是给出键被按下和释放的中断,其他的事情由软件来做。

虽然键盘与监视器在逻辑上是两个独立的设备,但是很多用户已经习惯于看到他们刚刚键入的字符出现在屏幕上。这个过程叫作回显(echoing)。

鼠标软件

大多数 PC 具有一个鼠标,或者具有一个轨迹球,轨迹球不过是躺在其背部上的鼠标。一种常见类型的鼠标在内部具有一个橡皮球,该橡皮球通过鼠标底部的一个圆洞突出,当鼠标在一个粗糙表面上移动时橡皮球会随着旋转。当橡皮球旋转时,它与放置在相互垂直的滚轴上的两个橡皮滚筒相摩擦。东西方向的运动导致平行于 y 轴的滚轴旋转,南北方向的运动导致平行于 x 轴的滚轴旋转。

另一种流行的鼠标类型是光学鼠标,它在其底部装备有一个或多个发光二极管和光电探测器。早期的光学鼠标必须在特殊的鼠标垫上操作,鼠标垫上刻有矩形的网格,这样鼠标能够计数穿过的线数。现代光学鼠标在其中有图像处理芯片并且获取处于它们下方的连续的低分辨率照片,寻找从图像到图像的变化。

当鼠标在随便哪个方向移动了一个确定的最小距离,或者按钮被按下或释放时,都会有一条消息发送给计算机。最小距离大约是 0.1 mm(尽管它可以在软件中设置)。有些人将这一单位称为一个鼠标步(mickey)。鼠标可能具有一个、两个或者三个按钮,这取决于设计者对于用户跟踪多个按钮的智力的估计。某些鼠标具有滚轮,可以将额外的数据发送回计算机。无线鼠标与有线鼠标相同,区别是无线鼠标使用低功率无线电,例如使用蓝牙(Bluetooth)标准将数据发送回计算机,而有线鼠标是通过导线将数据发送回计算机。

发送到计算机的消息包含三个项目:Ar、Ay、按钮,即自上一次消息之后 x 位置的变化、自上一次消息之后 y 位置的变化、按钮的状态。消息的格式取决于系统和鼠标所具有的按钮的数目。通常,消息占 3 字节。大多数鼠标返回报告最多每秒 40 次,所以鼠标自上一次报告之后可能移动了多个鼠标步。

注意,鼠标仅仅指出位置上的变化,而不是绝对位置本身。如果轻轻地拿起鼠标并且轻轻地放下而不导致橡皮球旋转,那么就不会有消息发出。

许多 GUI 区分单击与双击鼠标按钮。如果两次点击在空间上(鼠标步)足够接近,并且在时间上(毫秒)也足够接近,那么就会发出双击信号。最大的“足够接近”是软件的事情,并且这两个参数通常是用户可设置的。

输出软件

文本输出

当输出是连续的单一字体、大小和颜色的形式时,输出比输入简单。大体上,程序将字符发送到当前窗口,而字符在那里显示出来。通常,一个字符块或者一行是在一个系统调用中被写到窗口上的。

屏幕编辑器和许多其他复杂的程序需要能够以更加复杂的方式更新屏幕,例如在屏幕的中间替换一行。为满足这样的需要,大多数输出驱动程序支持一系列命令来移动光标,在光标处插入或者删除字符或行。这些命令常常被称为转义序列(escape sequence)。在 25 行 80 列 ASCII 哑终端的全盛期,有数百种终端类型,每一种都有自己的转义序列。因而,编写在一种以上的终端类型上工作的软件是十分困难的。

一种解决方案是称为 termcap 的终端数据库,它是在伯克利 UNIX 中引人的。该软件包定义了许多基本动作,例如将光标移动到(行,列)。为了将光标移动到一个特殊的位置,软件(如一个编辑器)使用一个一般的转义序列,然后该转义序列被转换成将要被执行写操作的终端的实际转义序列。以这种方式,该编辑器就可以工作在任何具有 termcap 数据库人口的终端上。许多 UNIX 软件仍然以这种方式工作,即使在个人计算机上。

逐渐地,业界看到了转义序列标准化的需要,所以就开发了一个 ANSI 标准。图 5-32 所示为一些该标准的取值。

X窗口系统

几乎所有 UNIX 系统的用户界面都以 X 窗口系统(X Window System)为基础,X 窗口系统经常仅称为 X,它是作为 Athena 计划 9 的一部分于 20 世纪 80 年代在 MIT 开发的。X 窗口系统具有非常好的可移植性,并且完全运行在用户空间中。人们最初打算将其用于将大量的远程用户终端与中央计算服务器相连接,所以它在逻辑上分成客户软件和主机软件,这样就有可能运行在不同的计算机上。在现代个人计算机上,两部分可以运行在相同的机器上。在 Linux 系统上,流行的 Gnome 和 KDE 桌面环境就运行在 X 之上。

图形用户界面

大多数个人计算机提供了 GUI (Graphical User Interface,图形用户界面)。

GUI 软件可以在用户级代码中实现(如 UNIX 系统所做的那样),也可以在操作系统中实现(如 Windows 的情况)。

GUI 系统的输入仍然使用键盘和鼠标,但是输出几乎总是送往特殊的硬件电路板,称为图形适配器(graphics adapter)。图形适配器包含特殊的内存,称为视频 RAM (video RAM),它保存出现在屏幕上的图像。图形适配器通常具有强大的 32 位或 64 位 CPU 和多达 1 GB 自己的 RAM,独立于计算机的主存。

位图

并不是计算机处理的所有图像都能够使用矢量图形学来生成。例如,照片和视频就不使用矢量图形学。反之,这些项目可以通过在图像上覆盖一~层网格扫描输入。每一个网格方块的平均红、绿、蓝取值被采样并且保存为一个像素的值。这样的文件称为位图(bitmap)。Windows 中有大量的工具用于处理位图。

字体

TrueType 字体不是位图而是字符的轮廓。每个TrueType 字符是通过围绕其周界的一系列点来定义的,所有的点都是相对于(0,0) 原点。使用这一系统,放大或者缩小字符是十分容易的,必须要做的全部事情只是将每个坐标乘以相同的比例因子。采用这种方法,TrueType 字符可以放大或者缩小到任意的点阵尺寸,甚至是分数点阵尺寸。一旦给定了适当的尺寸,各个点可以使用幼儿园教的著名的逐点连算法连接起来(注意现代幼儿园为了更加光滑的结果而使用曲线尺)。轮廓完成之后,就可以填充字符了。

触摸屏

电阻屏(resistive screen)顶部有一层柔性的塑料表面。

电容屏(capactive screen)有两层硬表面,一般是玻璃,每个面都镀有 ITO。典型的布局是让 ITO 以平行线方式添加到每个表面,并且顶层中的线与底层中的线相互垂直。

电阻屏不好的地方在于它们一般不能支持多点触控(multitouch),这是一种同时检测多个触点的技术。它允许你在屏幕上用两个或者更多的手指操作目标物。人(或许还有猩猩)喜欢多点触控,因为它使人可以用两个手指采用捏拢和张开的手势来放大或者缩小图像或文档。

瘦客户机

一个合理的结论大概是:大多数用户想要高性能的交互式计算,但是实在不想管理一台计算机。这一结论导致研究人员重新研究了分时系统使用的哑终端(现在文雅地称为瘦客户机(thin client)),它们符合现代终端的期望。X 是这一方向的一个步骤并且专用的 X 终端一度十分流行,但是它们现在已经失宠,因为它们的价格与 PC 相仿,能做的事情更少,并且仍然需要某些软件维护。圣杯(holy grail)应该是一个高性能的交互式计算系统,在该系统中用户的机器根本就没有软件。十分有趣的是,这一目标是可以达到的。

最著名的瘦客户机之一是 Chromebook,虽然它是由 Google 积极推进的,但是大大小小的制造商们提供了各种各样的型号。该笔记本运行 ChromeOS,它基于 Linux 和 Chrome Web 浏览器,并且假设永久在线。大多数其他软件以 Web App 的形式由 Web 作为宿主,这使得 Chromebook 上的软件栈本身与大多数传统笔记本电脑相比相当纤瘦。另一方面,由于运行完全的 Linux 栈以及 Chrome 浏览器,所以这样的系统也并不是 100%简洁的。

电源管理

使计算机使用较少的能量因而现有的电池能够持续更长的时间就高悬在每个人的议事日程之上。操作系统在这里扮演着主要的角色。

在最低的层次,硬件厂商试图使他们的电子装置具有更高的能量效率。使用的技术包括减少晶体管的尺寸、利用动态电压调节、使用低摆幅并隔热的总线以及类似的技术。这些内容超出了本书的范围,感兴趣的读者可以在 Venkatachalam 和 Franz (2005) 的论文中找到很好的综述。

存在两种减少能量消耗的一般方法。第一种方法是当计算机的某些部件(主要是I/O设备)不用的时候由操作系统关闭它们,因为关闭的设备使用的能量很少或者不使用能量。第二种方法是应用程序使用较少的能量,这样为了延长电池时间可能会降低用户体验的质量。我们将依次看一看这些方法,但是首先就电源使用方面谈一谈硬件设计。

硬件问题

电池般分为两种类型:一次性使用的和可再充电的。一次性使用的电池(AAA、AA 与 D 电池)可以用来运转掌上设备,但是没有足够的能量为具有大面积发光屏幕的笔记本电脑供电。相反,可再充电的电池能够存储足够的能量为笔记本电脑供电几个小时。在可再充电的电池中,镍镉电池曾经占据主导地位,但是它们后来让位给了镍氢电池,镍氢电池持续的时间更长并且当它们最后被抛弃时不如镍镉电池污染环境那么严重。锂电池更好一些,并且不需要首先完全耗尽就可以再充电,但是它们的容量同样非常有限。

大多数计算机厂商对于电池节约采取的一般措施是将CPU、内存以及I/O设备设计成具有多种状态: 工作、睡眠、休眠和关闭。要使用设备,它必须处于工作状态。当设备在短时间内暂时不使用时,可以将其置于睡眠状态,这样可以减少能量消耗。当设备在一个较长的时间间隔内不使用时,可以将其置于休眠状态,这样可以进一步减少能量消耗。这里的权衡是,使一个设备脱离休眠状态常常比使一个设备脱离睡眠状态花费更多的时间和能量。最后,当一个设备关闭时,它什么事情也不做并且也不消耗电能。并非所有的设备都具有这些状态,但是当它们具有这些状态时,应该由操作系统在正确的时机管理状态的变迁。

操作系统问题

操作系统在能量管理上扮演着一个重要的角色,它控制着所有的设备,所以它必须决定关闭什么设备以及何时关闭。如果它关闭了一个设备并且该设备很快再次被用户需要,可能在设备重启时存在恼人的延迟。另一方面,如果它等待了太长的时间才关闭设备,能量就白白地浪费了。

这里的技巧是找到算法和启发式方法,让操作系统对关于关闭什么设备以及何时关闭能够作出良好的决策。问题是“良好”是高度主观的。一个用户可能觉得在 30 s 未使用计算机之后计算机要花费 2 s 的时间响应击键是可以接受的。另一个用户在相同的条件下可能会发出一连串的诅咒。

应用程序问题

有关输入/输出的研究

关于输入/输出存在着相当数量的研究,其中一些研究集中在特定的设备上,而不是一般性的I/O。另外一些研究工作关注的是输入/输出的底层结构。比如,使用流水线结构来构建面向特定应用的输入/ 输出系统,减少复制、切换上下文、发送信号以及缓存和 TLB 利用不充分带来的额外开销(DeBruijn 等人,2011)。它建立在 Beltway Buffers 一种高级环式缓存的概念上 E,要比目前存在的缓存系统更加高效(DeBruijn 和 Bos, 2008)。流水线结构对于网络要求高的应用尤其适用。Megapipe (Han 等人, 2012)是另一个面向基于消息负载的网络输人/输出结构。它在内核空间和用户空间之间建立了每个核心的双向通道,在这种结构下系统层被抽象成像是轻量的 sockets。sockets 并不完全适合 POSIX的标准, 因此应用程序需要适应这种更加高效的输入/输出,并从中获益。

小结

输入/输出是一个经常被忽略但是十分重要的话题。任何一个操作系统都有大量的组分与I/O有关。I/O可以用三种方式来实现。第一是程序控制I/O,在这种方式下主CPU输人或输出每个字节或字并且闲置在一个密封的循环中等待,直到它能够获得或者发送下一个字节或字。第二是中断驱动的 IO,在这种方式下CPU针对一个字节或字开始I/O传送并且离开去做别的事情,直到一个中断到来发出信号通知 I/O完成。第三是DMA,在这种方式下有一个单独的芯片管理着一个数据块的完整传送过程,只有当整个数据块完成传送时才引发一个中断。

I/O可以组织成4个层次:中断服务程序、设备驱动程序、与设备无关的I/O软件和运行在用户空间

的 I/O 库与假脱机程序。设备驱动程序处理运行设备的细节并且向操作系统的其余部分提供统- -的接口。 与设备无关的I/O软件做类似缓冲与错误报告这样的事情。

盘具有多种类型,包括磁盘、RAID 和各类光盘。磁盘臂调度算法经常用来改进磁盘性能,但是虚拟几何规格的出现使事情变得十分复杂。通过将两块磁盘组成一对,可以构造稳定的存储介质,具有某些有用的性质。

时钟可以用于跟踪实际时间,限制进程可以运行多长时间,处理监视定时器,以及进行记账。面向字符的终端具有多种多样的问题,这些问题涉及特殊的字符如何输人以及特殊的转义序列如何输出。输人可以采用原始模式或加工模式,取决于程序对于输入需要有多少控制。针对输出的转义序列控制着光标的移动并且允许在屏幕上插入和删除文本。
大多数 UNIX 系统使用 X 窗口系统作为用户界面的基础。它包含与特殊的库相绑定并发出绘图命令的程序,以及在显示器上执行绘图的服务器。

许多个人计算机使用 GUI 作为它们的输出。GUI 基于 WIMP 范式:窗口、图标、菜单和定点设备。

基于 GUI 的程序一般是事件驱动的,当键盘事件、鼠标事件和其他事件发生时立刻会被发送给程序以便处理。在 UNIX 系统中,GUI 几乎总是运行在 X 之上。

瘦客户机与标准 PC 相比具有某些优势,对用户而言,值得注意的是简单性并且需要较少维护。最后,电源管理对于手机、平板电脑和笔记本电脑来说是一个主要的问题,因为电池寿命是有限的,而对台式机和服务器则意味着机构的电费账单。操作系统可以采用各种技术来减少功率消耗。通过牺牲某些质量以换取更长的电池寿命,应用程序也可以做出贡献。

说点什么

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  Subscribe  
提醒