计算机体系结构基础(第3版)
上QQ阅读APP看书,第一时间看更新

5.4 处理器和IO设备间的通信

前面介绍了组成计算机系统的各个部分,在冯·诺依曼结构中,处理器(准确地说是内部的控制器)处于中心位置,需要控制其他各个部件的运行。

对存储器的控制是通过读写指令来完成的。存储器是存储单元阵列,对某个地址的读写不会影响其他存储单元。

而IO设备大都是具有特定功能的部件,不能当作简单的存储阵列来处理。由于IO设备的底层控制相当复杂,它们一般都是由一个设备控制器进行控制。设备控制器会提供一组寄存器接口,寄存器的内容变化会引起设备控制器执行一系列复杂的动作。设备控制器的接口寄存器也被称为IO寄存器。处理器通过读写IO寄存器来访问设备。写入这些寄存器的数据,会被设备控制器解析成命令,因此有些情况下将处理器对IO寄存器的访问称为命令字。处理器对内存和IO的访问模式有所不同,对访问的延迟和带宽需求也有较大差异。现代计算机系统的程序和数据都存放在内存中,内存访问性能直接影响处理器流水线的执行效率,也正是因为这样,才导致了各个Cache层次的出现。对处理器的内存访问来说,要求是高带宽和低延迟。IO设备一般用于外部交互,而IO操作一般会要求顺序的访问控制,从而导致执行效率低下,访问带宽低,延迟高,只能通过IO的DMA操作来提升性能。IO的DMA操作也是访问内存,因为DMA访存模式一般是大块的连续数据读写,所以对带宽的需求远高于对延迟的需求。

5.4.1 IO寄存器寻址

为了访问IO寄存器,处理器必须能够寻址这些寄存器。IO寄存器的寻址方式有两种:内存映射IO和特殊IO指令。

内存映射IO是把IO寄存器的地址映射到内存地址空间中,这些寄存器和内存存储单元被统一编址。读写IO地址和读写内存地址使用相同的指令来执行。处理器需要通过它所处的状态来限制应用程序可以访问的地址空间,使其不能直接访问IO地址空间,从而保证应用程序不能直接操作IO设备。与内存映射IO不同,特殊IO指令使用专用指令来执行IO操作。因此,IO地址空间可以和内存地址空间重叠,但实际指向不同的位置。操作系统可以通过禁止应用程序执行IO指令的方式来阻止应用程序直接访问IO设备。MIPS或LoongArch结构并没有特殊IO指令,通过普通的访存指令访问特定的内存地址空间进行IO访问。而X86结构使用专门的IO指令来执行IO操作。

5.4.2 处理器和IO设备之间的同步

处理器和IO设备之间需要协同工作,通过一系列软件程序来共同发挥设备功能。处理器和IO设备之间的同步有两种方式:查询和中断。

处理器通过向IO寄存器写入命令字来控制IO设备。大部分的控制操作不是通过一次寄存器写入就能完成的,处理器一般需要对IO寄存器进行多次访问,才能完成一次任务。绝大多数设备的IO寄存器不是无条件写入的,处理器在写入命令字之前,先要获取设备的当前状态,只有当设备进入特定的状态后,处理器才能执行特定的操作,这些特定的软件操作流程是在驱动程序中实现的。比如,对于一台打印机,打印机控制器会提供两个寄存器:数据寄存器和状态寄存器。数据寄存器用来存放当前需要打印的数据,状态寄存器用来指示打印机的状态,它包含两个基本位:完成位和错误位。完成位表示上一个字符打印完毕,可以打印下一个字符;错误位用来在打印机出现异常时指示出错的状态,比如卡纸或者缺纸。处理器在打印一串数据时,首先把数据写入数据寄存器,然后不断读取状态寄存器的值,当读出的完成位等于1时,才能把下一个字符写入数据寄存器。同时,处理器还需要检查错误位的值,当发生错误时,去执行对应的错误处理程序。

前面描述的打印过程就是查询方式的一个例子。当使用查询方式时,处理器向IO设备发出访问请求后,需要不断读取IO设备的状态寄存器,所以查询方式也被称为轮询。由于IO设备的速度一般都较慢,使用查询方式会浪费处理器的指令周期。而且,执行轮询的处理器无法同时执行其他工作,造成了性能的浪费。

为了解决查询方式效率较低的问题,中断方式被引入计算机系统。在中断方式下,处理器不需要轮询状态寄存器的值,而是在等待设备完成某个操作时转去执行其他进程。当设备完成某个操作后,自行产生一个中断信号来中断处理器的执行。处理器被中断后,再去读取设备的状态寄存器。中断方式将处理器从等待IO中解放了出来,大大提高了处理器的利用率,因此现代计算机的绝大部分IO设备都支持中断方式。

中断本质上是IO设备对处理器发出的一个信号,让处理器知道此时有数据传输需要或者已经发生数据传输。CPU收到中断信号后,会暂停当前CPU的执行进程,转去执行某个特定的程序。中断的一般过程为:

1)中断源发出中断信号到中断控制器;

2)中断控制器产生中断请求给CPU;

3)CPU发出中断响应,并读取中断类型码;

4)CPU根据中断类型码执行对应的中断服务程序;

5)CPU从中断服务程序返回,中断结束。

中断源即中断的源头,比如用户敲击一下键盘,单击一下鼠标,或者DMA的一次传输完成了,对应的控制器会产生一个中断信号。中断信号可以是一根信号线,也可以是一个消息包。这个中断信息会传送到中断控制器中。中断控制器是负责中断汇集、记录和转发的硬件逻辑。中断控制器一般都具有可编程功能,因此被称为可编程中断控制器(Programmable Interrupt Controller,简称PIC)。典型的中断控制器如Intel的8259A。8259A支持中断嵌套和中断优先级,可以支持8个中断源,并可以通过级联的方式进行扩展。

8259A内部包含3个寄存器:中断请求寄存器(Interrupt Request Register,简称IRR),用来存放当前的中断请求;中断在服务寄存器(In-Service Register,简称ISR),用来存放当前CPU正在服务的中断请求;中断Mask寄存器(Interrupt Mask Register,简称IMR),用来存放中断屏蔽位。

当中断源产生中断信号后,会将中断请求寄存器的某一位设置为1,如果该位没有被屏蔽,则产生一个中断信号(比如中断线)给处理器。处理器检测到该中断信号,并跳转到固定的地址执行中断服务例程。在中断服务例程中,处理器通过读取8259A获得中断向量号,进而调用对应的中断服务程序。在中断服务程序返回之前,要保证本次中断的中断信号被清除掉,否则CPU从中断服务程序返回之后,会被再次触发中断。8259A在中断响应时会自动将IRR的对应位复位。对于电平触发的中断,中断服务程序一般会读写中断源的相关寄存器,从而保证在中断返回之前,中断源的中断信号被撤掉,这样8259A的中断请求寄存器的对应位不会被再次置位。对于脉冲触发的中断,则不需要对设备IO寄存器进行处理。

5.4.3 存储器和IO设备之间的数据传送

存储器和IO设备之间需要进行大量的数据传输。例如,系统在启动时,需要把操作系统代码从硬盘搬运到内存中;计算机想要输出图形时,需要把准备显示的数据从内存搬运到显示控制器中。

那么存储器和IO设备之间是如何进行数据交换的呢?

早期,存储器和IO设备之间的数据传送都是由处理器来完成的。由于存储器和IO设备之间没有直接的数据通路,当需要从存储器中搬运数据到IO设备时,处理器首先从存储器中读数据到通用寄存器中,再从通用寄存器写数据到IO设备中;当需要从IO设备搬运数据到存储器中时,处理器要先从IO设备中读数据到通用寄存器,再从通用寄存器写入内存。这种方式称为PIO(Programming Input/Output)模式。

由于IO访问的访问延迟一般较大,而且IO访问之间需要严格的顺序关系,因而PIO方式的带宽较低。PIO模式存在两种同步方式:查询方式和中断方式。虽然中断方式可以降低处理器查询的开销,但当进行大量数据传输时,PIO模式仍然需要占用大量的处理器时间。使用中断方式,每传送一定的数据后都要进入一次中断,在中断服务程序中真正用于数据传送的时间可能并不多,大量的时间被用于断点保护、中断向量查询、现场恢复、中断返回等辅助性工作。对于一些数据传送速率较快的设备,PIO方式可能会因为处理器搬运数据速度较慢而降低数据的传送速度,因此PIO方式一般用于键盘、鼠标等低速设备。

在PIO方式中,数据要经过处理器内部的通用寄存器进行中转。中转不仅影响处理器的执行,也降低了数据传送的速率。如果在存储器和IO设备之间开辟一条数据通道,专门用于数据传输,就可以将处理器从数据搬运中解放出来。这种方式就是直接存储器访问(Direct Memory Access,简称DMA)方式。DMA方式在存储器和外设之间开辟直接的数据传送通道,数据传送由专门的硬件来控制。控制DMA数据传送的硬件被称为DMA控制器。

使用DMA进行传输的一般过程为:

1)处理器为DMA请求预先分配一段地址空间。

2)处理器设置DMA控制器参数。这些参数包括设备标识、数据传送的方向、内存中用于数据传送的源地址或目标地址、传输的字节数量等。

3)DMA控制器进行数据传输。DMA控制器发起对内存和设备的读写操作,控制数据传输。DMA传输相当于用IO设备直接续写内存。

4)DMA控制器向处理器发出一个中断,通知处理器数据传送的结果(成功或者出错以及错误信息)。

5)处理器完成本次DMA请求,可以开始新的DMA请求。

DMA方式对于存在大量数据传输的高速设备是一个很好的选择,硬盘、网络、显示等设备普遍都采用DMA方式。一个计算机系统中通常包含多个DMA控制器,比如有特定设备专用的SATA接口DMA控制器、USB接口DMA控制器等,也有通用的DMA控制器用于可编程的源地址与目标地址之间的数据传输。

DMA控制器的功能可以很简单,也可以很复杂。例如,DMA控制器可以仅仅支持对一段连续地址空间的读写,也可以支持对多段地址空间的读写以及执行其他的IO操作。不同IO设备的DMA行为各不相同,因此现代的IO控制器大多会实现专用的DMA控制器用于自身的数据传输。

表5.2举例说明了PIO和DMA两种数据传输方式的不同。

表5.2 PIO和DMA两种数据传输方式

从上面两个例子中可以看到,PIO方式和DMA方式处理的流程一致,区别在于:首先键盘的数据是被记录在IO设备本身的,而网卡的数据则直接由网卡写入内存之中;其次CPU处理时,对键盘是直接从IO寄存器读数据,而对网卡则直接从内存读数据。

看起来似乎差别不大。但需要考虑的是,IO访问相比内存访问慢很多,而且对于内存访问,CPU可以通过Cache、预取等方式进行加速,IO访问则缺少这种有效的优化方式。在上面的例子中,如果网卡采用PIO的方式使用CPU,对网卡的包一个字一个字地进行读访问,效率将非常低下。而对于键盘来说,一次输入仅仅只有8位数据,而且相比处理器的处理速度,键盘输入的速度相当低,采用PIO的处理方式能够很简单地完成数据输入任务。

5.4.4 龙芯3A3000+7A1000桥片系统中的CPU、GPU、DC通信

下面以龙芯3A3000+7A1000桥片中CPU、GPU、DC间的同步与通信为例说明处理器与IO间的通信。如图5.11所示,龙芯3A3000处理器和龙芯7A1000桥片通过HyperTransport总线相连,7A1000桥片中集成GPU、DC(显示控制器)以及专供GPU和DC使用的显存控制器。CPU可以通过PIO方式读写GPU中的控制寄存器、DC中的控制寄存器以及显存;GPU和DC可以通过DMA方式读写内存,GPU和DC还可以读写显存。

图5.11 龙芯3A3000+7A1000两片方案

CPU或GPU周期性地把要显示的数据写入帧缓存(Frame Buffer),DC根据帧缓存的内容进行显示。帧缓存可以分配在内存中,GPU和DC通过DMA方式访问内存中的帧缓存;在独立显存的情况下,帧缓存分配在独立显存中,CPU直接把要显示的数据写入帧缓存,或者GPU通过DMA方式从内存中读取数据并把计算结果写入帧缓存,DC直接读取帧缓存的内容进行显示。根据是否由GPU完成图形计算以及帧缓存是否分配在内存中,常见的显示模式有以下四种。

模式一:不使用GPU,CPU与DC共享内存。不使用桥片上的显存,而在内存中分配一个区域专供显示使用,这个区域称之为帧缓存。需要显示时,CPU通过正常内存访问将需要显示的内容写入内存中的帧缓存,然后通过PIO方式读写DC中的控制寄存器启动DMA,DC通过DMA操作读内存中的帧缓存并进行显示,如图5.12a所示。

模式二:不使用GPU,DC使用独立显存。DC使用桥片上的显存,这个区域称之为帧缓存。需要显示时,CPU将需要显示的内容从内存读出,再通过PIO方式写入独立显存上的帧缓存,然后通过PIO操作读写DC中的控制寄存器启动DMA,DC读显存上的帧缓存并进行显示,如图5.12b所示。

模式三:CPU与GPU/DC共享内存。需要显示时,CPU在内存中分配GPU使用的空间,并将相关数据填入,然后CPU通过PIO读写GPU中的控制寄存器启动DMA操作,GPU通过DMA读内存并将计算结果通过DMA写入内存中的帧缓存,CPU通过PIO方式读写DC中的控制寄存器启动DMA,DC通过DMA方式读内存中的帧缓存并完成显示,如图5.12c所示。

模式四:GPU/DC使用独立显存。需要显示时,CPU在内存中分配GPU使用的空间,并将相关数据填入,然后CPU通过PIO读写GPU中的控制寄存器启动DMA操作,GPU通过DMA读内存并将计算结果写入显存中的帧缓存,DC读显存中的帧缓存并完成显示,如图5.12d所示。

图5.12 3A3000+7A1000的不同显示方式