# (PART) 计算机硬件结构 {-}

# 计算机组成原理和结构

前面章节介绍的计算机指令系统结构从软件的角度描述了计算机功能，从本章开始将介绍计算机组成结构，从硬件的角度来看看计算机是怎样构成的。

如果说图灵机是现代计算机的计算理论模型，冯·诺依曼结构就是现代计算机的结构理论模型。本章从冯·诺依曼的理论模型开始，介绍计算机系统的各个组成部分，并与现代计算机的具体实现相对应。

## 冯·诺依曼结构

现代计算机都采用存储程序结构，又称为冯·诺依曼结构，是1945年匈牙利籍数学家冯·诺依曼受宾夕法尼亚大学研制的ENIAC计算机结构的启发提出的，是世界上第一个完整的计算机体系结构。

冯·诺依曼结构的主要特点是：①计算机由存储器、运算器、控制器、输入设备和输出设备五部分组成，其中运算器和控制器合称为中央处理器（Central Processing Processor，简称CPU）。②存储器是按地址访问的线性编址的一维结构，每个单元的位数固定。③采用存储程序方式，即指令和数据不加区别混合存储在同一个存储器中。④控制器通过执行指令发出控制信号控制计算机的操作。指令在存储器中按其执行顺序存放，由指令计数器指明要执行的指令所在的单元地址。指令计数器一般按顺序递增，但执行顺序可按运算结果或当时的外界条件而改变。⑤以运算器为中心，输入输出设备与存储器之间的数据传送都经过运算器。冯·诺依曼计算机的工作原理如图\@ref(fig:von-architecture)所示。

```{r von-architecture, echo=FALSE, fig.align='center', fig.cap="冯·诺依曼计算机体系结构", out.width='50%'}
knitr::include_graphics("images/chapter5/von_architecture.png")
```

随着技术的进步，冯·诺依曼结构得到了持续的改进，主要包括以下几个方面：①由以运算器为中心改进为以存储器为中心。使数据的流向更加合理，从而使运算器、存储器和输入输出设备能够并行工作。②由单一的集中控制改进为分散控制。计算机发展初期，工作速度很低，运算器、存储器、控制器和输入输出设备可以在同一个时钟信号的控制下同步工作。现在运算器、内存与输入输出设备的速度差异很大，需要采用异步方式分散控制。③从基于串行算法改进为适应并行算法。出现了流水线处理器、超标量处理器、向量处理器、多核处理器、对称多处理器（Symmetric Multiprocessor，简称SMP）、大规模并行处理机（Massively Parallel Processing，简称MPP）和机群系统等。④出现为适应特殊需要的专用计算机，如图形处理器（Graphic Processing Unit，简称GPU）、数字信号处理器（Digital Signal Processor，简称DSP）等。⑤在非冯·诺依曼计算机的研究方面也取得一些成果，如依靠数据驱动的数据流计算机、图归约计算机等。


虽然经过了长期的发展，现代计算机系统占据主要地位的仍然是以存储程序和指令驱动执行为主要特点的冯·诺依曼结构。

作为冯·诺依曼结构的一个变种，哈佛结构把程序和数据分开存储。控制器使用两条独立的总线读取程序和访问数据，程序空间和数据空间完成分开。在通用计算机领域，由于应用软件的多样性，要求计算机不断地变化所执行的程序内容，并且频繁地对数据与程序占用的存储器资源进行重新分配，使用统一编址可以最大限度地利用资源。但是在嵌入式应用中，系统要执行的任务相对单一，程序一般是固化在硬件里的，同时嵌入式系统对安全性、可靠性的要求更高，哈佛结构独立的程序空间更有利于代码保护。因此，在嵌入式领域，哈佛结构得到了广泛应用。需要指出的是，哈佛结构并没有改变冯·诺依曼结构存储程序和指令驱动执行的本质，它只是冯·诺依曼结构的一个变种，并不是独立于冯·诺依曼结构的一种新型结构。

## 计算机的组成部件

本节对计算机的主要组成部件进行介绍。按照冯·诺依曼结构，计算机包含五大部分，即运算器、控制器、存储器、输入设备和输出设备。

### 运算器

运算器是计算机中负责计算（包括算术计算和逻辑计算等）的部件。运算器包括算术和逻辑运算部件（Arithmetic Logic Units，简称ALU）、移位部件、浮点运算部件（Floating Point Units，简称FPU）、向量运算部件、寄存器等。其中，复杂运算如乘除法、开方及浮点运算可用程序实现或由运算器实现。寄存器既可用于保存数据，也可用于保存地址。运算器还可设置条件码寄存器等专用寄存器，条件码寄存器保存当前运算结果的状态，如运算结果是正数、负数或零，是否溢出等。

运算器支持的运算类型经历了从简单到复杂的过程。最初的运算器只有简单的定点加减和基本逻辑运算，复杂运算如乘除通过加减、移位指令构成的数学库完成；后来逐渐出现硬件定点乘法器和除法器。在早期的微处理器中，浮点运算器以协处理器的形式出现在计算机中（如Intel 8087协处理器），包含二进制浮点数的加、减、乘、除等运算，现代的通用微处理器则普遍包含完整的浮点运算部件。20世纪90年代开始，微处理器中出现了单指令多数据（Single Instruction Multiple Data，简称SIMD）的向量运算器，部分处理器还实现了超越函数硬件运算单元，如sin、cos、exp、log等。部分用于银行业务处理的计算机（如IBM Power系列）还实现了十进制定、浮点数的运算器。

随着晶体管集成度的不断提升，处理器中所集成的运算器的数量也持续增加，通常将具有相近属性的一类运算组织在一起构成一个运算单元。不同的处理器有不同的运算单元组织，有的倾向于每个单元大而全，有的倾向于每个单元的功能相对单一。处理器中包含的运算单元数目也逐渐增加，从早期的单个运算单元逐渐增加到多个运算单元。由于运算单元都需要从寄存器中读取操作数，并把结果写回寄存器，因此处理器中运算单元的个数主要受限于寄存器堆读写端口个数。运算单元一般按照定点、浮点、访存、向量等大类来组织，也有混合的，如SIMD部件既能做定点也能做浮点运算，定点部件也可以做访存地址计算等。

表\@ref(tab:alu)给出了几种经典处理器的运算器结构。其中Alpha 21264、MIPS R10000、HP PA8700、Ultra Sparc III、Power 4是20世纪90年代RISC处理器鼎盛时期经典的微处理器，而Intel Skylake、AMD Zen、Power 8、龙芯3A5000则是最新处理器。

```{r alu, echo = FALSE, message=FALSE, tab.cap='经典处理器的运算器结构'}
autonum <- run_autonum(seq_id = "tab", bkm = "alu", bkm_all = TRUE)
readr::read_csv('./materials/chapter5/alu.csv') %>%
flextable() %>%
set_caption(caption="经典处理器的运算器结构", autonum = autonum) %>%
width(j=1, width=1.3) %>%
width(j=2, width=2.0) %>%
width(j=3, width=2.6) %>%
theme_box()
```

### 控制器

控制器是计算机中发出控制命令以控制计算机各部件自动、协调地工作的装置。控制器控制指令流和每条指令的执行，内含程序计数器和指令寄存器等。程序计数器存放当前执行指令的地址，指令寄存器存放当前正在执行的指令。指令通过译码产生控制信号，用于控制运算器、存储器、IO设备的工作以及后续指令的获取。这些控制信号可以用硬连线逻辑产生，也可以用微程序产生，也可以两者结合产生。为了获得高指令吞吐率，可以采用指令重叠执行的流水线技术，以及同时执行多条指令的超标量技术。当遇到执行时间较长或条件不具备的指令时，把条件具备的后续指令提前执行（称为乱序执行）可以提高流水线效率。控制器还产生一定频率的时钟脉冲，用于计算机各组成部分的同步。

由于控制器和运算器的紧密耦合关系，现代计算机通常把控制器和运算器集成在一起，称为中央处理器，即CPU。随着芯片集成度的不断提高，现代CPU除了含有运算器和控制器外，常常还集成了其他部件，比如高速缓存（Cache）部件、内存控制器等。

计算机执行指令一般包含以下过程：从存储器取指令并对取回的指令进行译码，从存储器或寄存器读取指令执行需要的操作数，执行指令，把执行结果写回存储器或寄存器。上述过程称为一个指令周期。计算机不断重复指令周期直到完成程序的执行。体系结构研究的一个永恒主题就是不断加速上述指令执行周期，从而提高计算机运行程序的效率。由于控制器负责控制指令流和每条指令的执行，对提高指令执行效率起着至关重要的作用。

现代处理器的控制器都通过指令流水线技术来提高指令执行效率。指令流水线把一条指令的执行划分为若干阶段（如分为取指、译码、执行、访存、写回阶段）来减少每个时钟周期的工作量，从而提高主频；并允许多条指令的不同阶段重叠执行实现并行处理（如一条指令处于执行阶段时，另一条指令处于译码阶段）。虽然同一条指令的执行时间没有变短，但处理器在单位时间内执行的指令数增加了。

计算机中的取指部件、运算部件、访存部件都在流水线的调度下具体执行指令规定的操作。运算部件的个数和延迟，访存部件的存储层次、容量和带宽，以及取指部件的转移猜测算法等是决定微结构性能的重要因素。常见的提高流水线效率的技术包括转移预测技术、乱序执行技术、超标量（又称为多发射）技术等。

（1）转移预测技术。冯·诺依曼结构指令驱动执行的特点，使转移指令成为提高流水线效率的瓶颈。典型应用程序平均每5-10条指令中就有一条转移指令，而转移指令的后续指令需要等待转移指令执行结果确定后才能取指，导致转移指令和后续指令之间不能重叠执行，降低了流水线效率。随着主频的提高，现代处理器流水线普遍在10-20级之间，由于转移指令引起的流水线阻塞成为提高指令流水线效率的重要瓶颈。

转移预测技术可以消除转移指令引起的指令流水线阻塞。转移预测器根据当前转移指令或其他转移指令的历史行为，在转移指令的取指或译码阶段预测该转移指令的跳转方向和目标地址并进行后续指令的取指。转移指令执行后，根据已经确定的跳转方向和目标地址对预测结果进行修正。如果发生转移预测错误，还需要取消指令流水线中的后续指令。为了提高预测精度并降低预测错误时的流水线开销，现代高性能处理器采用了复杂的转移预测器。

例如，可以在取指部件中设置一位标志记录上一条转移指令的跳转方向，碰到转移指令，不用等该转移指令执行结果，就根据该标志猜测跳转方向进行取指。对于C语言中的`for (i=0,i<N,i++)`类的循环，这种简单的转移猜测就可以达到（N-1）/（N+1）的准确度，当N很大时准确度很高。

（2）乱序执行技术。如果指令i是条长延迟指令，如除法指令或Cache不命中的访存指令，那么在顺序指令流水线中指令i后面的指令需要在流水线中等待很长时间。乱序执行技术通过指令动态调度允许指令i后面的源操作数准备好的指令越过指令i执行（需要使用指令i的运算结果的指令由于源操作数没有准备好，不会越过指令i执行），以提高指令流水线效率。为此，在指令译码之后的读寄存器阶段，应判断指令需要的操作数是否准备好。如果操作数已经准备好，就进入执行阶段；如果操作数没有准备好，就进入称为保留站或者发射队列的队列中等待，直到操作数准备好后再进入执行阶段。为了保证执行结果符合程序规定的要求，乱序执行的指令需要有序结束。为此，执行完的指令均进入一个称为重排序缓冲（Re-Order Buffer，简称ROB）的队列，并把执行结果临时写入重命名寄存器。ROB根据指令进入流水线的次序，有序提交指令的执行结果到目标寄存器或存储器。CDC6600和IBM 360/91分别使用记分板和保留站最早实现了指令的动态调度。

就像保留站和重排序缓冲用来临时存储指令以使指令在流水线中流动更加通畅，重命名寄存器用来临时存储数据以使数据在流水线流动更加通畅。保留站、重排序缓冲、重命名寄存器都是微结构中的数据结构，程序员无法用指令来访问，是结构设计人员为了提高流水线效率而用来临时存储指令和数据的。其中，保留站把指令从有序变为无序以提高执行效率，重排序缓存把指令从无序重新变为有序以保证正确性，重命名寄存器则在乱序执行过程中临时存储数据。重命名寄存器与指令可以访问的结构寄存器（如通用寄存器、浮点寄存器）相对应。乱序执行流水线把指令执行结果写入重命名寄存器而不是结构寄存器，以避免破坏结构寄存器的内容，到顺序提交阶段再把重命名寄存器内容写入结构寄存器。两组执行不同运算但使用同一结构寄存器的指令可以使用不同的重命名寄存器，从而避免该结构寄存器成为串行化瓶颈，实现并行执行。

（3）超标量技术。工艺技术的发展使得在20世纪80年代后期出现了超标量处理器。超标量结构允许指令流水线的每一阶段同时处理多条指令。例如Alpha 21264处理器每拍可以取4条指令，发射6条指令，写回6条指令，提交11条指令。如果把单发射结构比作单车道马路，多发射结构就是多车道马路。

由于超标量结构的指令和数据通路都变宽了，使得寄存器端口、保留站端口、ROB端口、功能部件数都需要增加，例如Alpha 21264的寄存器堆有8个读端口和6个写端口，数据Cache的RAM通过倍频支持一拍两次访问。现代超标量处理器一般包含两个以上访存部件，两个以上定点运算部件以及两个以上浮点运算部件。超标量结构在指令译码或寄存器重命名时不仅要判断前后拍指令的数据相关，还需要判断同一拍中多条指令间的数据相关。

### 存储器

存储器存储程序和数据，又称主存储器或内存，一般用动态随机访问存储器（Dynamic Random Access Memory，简称DRAM）实现。CPU可以直接访问它，IO设备也频繁地与它交换数据。存储器的存取速度往往满足不了CPU的快速要求，容量也满足不了应用的需要，为此将存储系统分为高速缓存（Cache)、主存储器和辅助存储器三个层次。Cache存放当前CPU最频繁访问的部分主存储器内容，可以采用比DRAM速度快但容量小的静态随机访问存储器（Static Random Access Memory，简称SRAM）实现。数据和指令在Cache和主存储器之间的调动由硬件自动完成。为扩大存储器容量，使用磁盘、磁带、光盘等能存储大量数据的存储器作为辅助存储器。计算机运行时所需的应用程序、系统软件和数据等都先存放在辅助存储器中，在运行过程中分批调入主存储器。数据和指令在主存储器和辅助存储器之间的调动由操作系统完成。CPU访问存储器时，面对的是一个高速（接近于Cache的速度）、大容量（接近于辅助存储器的容量）的存储器。现代计算机中还有少量只读存储器（Read Only Memory，简称ROM）用来存放引导程序和基本输入输出系统（Basic Input Output System，简称BIOS）等。现代计算机访问内存时采用虚拟地址，操作系统负责维护虚拟地址和物理地址转换的页表，集成在CPU中的存储管理部件（Memory Management Unit，简称MMU）负责把虚拟地址转换为物理地址。

存储器的主要评价指标为存储容量和访问速度。存储容量越大，可以存放的程序和数据越多。访问速度越快，处理器访问的时间越短。对相同容量的存储器，速度越快的存储介质成本越高，而成本越低的存储介质则速度越低。目前人们发明的用于计算机系统的存储介质主要包括以下几类：

1）磁性存储介质。如硬盘、磁带等，特点是存储密度高、成本低、具有非易失性（断电后数据可长期保存），缺点是访问速度慢。磁带的访问速度在秒级，磁盘的访问速度一般在毫秒级，这样的访问速度显然不能满足现代处理器纳秒级周期的速度要求。

2）闪存（Flash Memory）。同样是非易失性的存储介质，与磁盘相比，它们的访问速度快，成本高，容量小。随着闪存工艺技术的进步，闪存芯片的集成度不断提高，成本持续降低，闪存正在逐步取代磁盘作为计算机尤其是终端的辅助存储器。

3）动态随机访问存储器（DRAM）。属于易失性存储器（断电后数据丢失）。特点是存储密度较高（存储一位数据只需一个晶体管），需要周期性刷新，访问速度较快。其访问速度一般在几十纳秒级。

4）静态随机访问存储器（SRAM）。属于易失性存储器（断电后数据丢失）。存储密度不如DRAM高（SRAM存储一位数据需要4-8个晶体管），不用周期性刷新，但访问速度比DRAM快，可以达到纳秒级，小容量时能够和处理器核工作在相同的时钟频率。

现代计算机中把上述不同的存储介质组成存储层次，以在成本合适的情况下降低存储访问延迟，如图\@ref(fig:storage-hierarchy)中所示，越往上的层级，速度越快，但成本越高，容量越小；越往下的层级，速度越慢，但成本越低，容量越大。图\@ref(fig:storage-hierarchy)所示存储层次中的寄存器和主存储器直接由指令访问，Cache缓存主存储器的部分内容；而非易失存储器既是辅助存储器，又是输入输出设备，非易失存储器的内容由操作系统负责调入调出主存储器。

```{r storage-hierarchy, echo=FALSE, fig.align='center', fig.cap="存储层次", out.width='50%'}
knitr::include_graphics("images/chapter5/storage_hierarchy.png")
```

存储层次的有效性，依赖于程序的访存局部性原理，包含两个方面：一是时间局部性，指的是如果一个数据被访问，那么在短时间内很有可能被再次访问；二是空间局部性，指的是如果一个数据被访问，那么它的邻近数据也很有可能被访问。利用局部性原理，可以把程序近期可能用到的数据存放在靠上的层次，把近期内不会用到的数据存放在靠下的层次。通过恰当地控制数据在层次间的移动，使处理器需要访问的数据尽可能地出现在靠近处理器的存储层次，可以大大提高处理器获得数据的速度，从而近似达到用最快的存储器构建一个容量很大的单级存储的效果。现代计算机一般使用多端口寄存器堆实现寄存器，使用SRAM来构建片上的高速缓存（Cache），使用DRAM来构建程序的主存储器（也称为主存、内存），使用磁盘或闪存来构建大容量的存储器。

1.高速缓存

随着工艺技术的发展，处理器的运算速度和内存容量按摩尔定律的预测指数增加，但内存速度提高非常缓慢，与处理器速度的提高形成了“剪刀差”。工艺技术的上述特点使得访存延迟成为以存储器为中心的冯·诺依曼结构的主要瓶颈。Cache技术利用程序访问内存的时间局部性（一个单元如果当前被访问，则近期很有可能被访问）和空间局部性（一个单元被访问后，与之相邻的单元也很有可能被访问），使用速度较快、容量较小的Cache临时保存处理器常用的数据，使得处理器的多数访存操作可以在Cache上快速进行，只有少量访问Cache不命中的访存操作才访问内存。

Cache是内存的映像，其内容是内存内容的子集，处理器访问Cache和访问内存使用相同的地址。从20世纪80年代开始，RISC处理器就开始在处理器芯片内集成KB级的小容量Cache。现代处理器则普遍在片内集成多级Cache，典型的多核处理器的每个处理器核中一级指令Cache和数据Cache各几十KB，二级Cache为几百KB，而多核共享的三级Cache为几MB到几十MB。现代处理器访问寄存器时一拍可以同时读写多个数据，访问一级Cache延迟为1-4拍，访问二级Cache延迟为10-20拍，访问三级Cache延迟为40-60拍，访问内存延迟为100-200拍。

CPU执行一个程序的时间可以描述为：程序运行的总动态指令数 * CPI * 时钟周期。其中CPI（Cycle Per Instruction）表示平均每条指令执行花费的时钟周期数。CPI可以进一步细分为每种类型指令的CPI与这类指令占总指令数比例乘积之和，如运算指令CPI * 运算指令比例 + 访存指令CPI * 访存指令比例 + 其他指令CPI * 其他指令比例。访存指令CPI也称为平均访问延迟AMAT（Average Memory Access Latency）。在具有高速缓存的计算机中，

$$AMAT = HitTime + MissRate * MissPenalty$$

其中HitTime表示高速缓存命中时的访问延迟，MissRate表示高速缓存失效率，MissPenalty表示高速缓存失效时额外的访问延迟。例如，在某计算机系统中HitTime=1, MissRate=5%, MissPenalty=100，则AMAT=1+5=6。



2.内存

主存储器又称为内存。内存的读写速度对计算机的整体性能影响重大。为了提升处理器的访存性能，现代通用处理器都将内存控制器与CPU集成在同一芯片内，以减小平均访存延迟。

现代计算机的内存一般都采用同步动态随机存储器（SDRAM）实现。DRAM的一个单元由MOS管T和电容C（存储单元）组成，如图\@ref(fig:dram-cell)所示。电容C存储的电位决定存储单元的逻辑值。单元中的字线根据读写地址译码得到，连接同一字的若干位；单元中的位线把若干字的同一位链接在一起。进行读操作时，先把位线预充到Vref=VCC/2，然后字线打开T管，C引起差分位线微小的电位差，感应放大器读出，读出后C中的电位被破坏，需要把读出值重新写入C。进行写操作时，先把位线预充成要写的值，然后打开字线，把位线的值写入C。C中的电容可能会漏掉，因此DRAM需要周期刷新，刷新可以通过读操作进行，一般每行几十微秒刷新一次。
"
```{r dram-cell, echo=FALSE, fig.align='center', fig.cap="DRAM的单元读写原理", out.width='50%'}
knitr::include_graphics("images/chapter5/dram_cell.png")
```

SDRAM芯片一般采用行列地址线复用技术，对SDRAM进行读写时，需要先发送行地址打开一行，再发送列地址读写需要访问的存储单元。为了提高访问的并发度，SDRAM芯片一般包含多个Bank（存储块），这些Bank可以并行操作。图\@ref(fig:sdram-structure)显示了一个DDR2 SDRAM x8芯片的内部结构图。可以看到，该SDRAM内部包含了8个Bank，每个Bank对应一个存储阵列和一组感应放大器，所有的Bank共用读锁存（Read Latch）和写FIFO。

对SDRAM进行写操作后，由于必须等到写数据从IO引脚传送到对应Bank的感应放大器后，才能进行后续的预充电操作（针对相同Bank）或者读操作（针对所有Bank），因此写操作会给后续的其他操作带来较大的延迟，但连续的写操作却可以流水执行。为了降低写操作带来的开销，内存控制器往往将多个写操作聚集在一起连续发送，以分摊单个写操作的开销。

```{r sdram-structure, echo=FALSE, fig.align='center', fig.cap="SDRAM的功能结构图", out.width='100%'}
knitr::include_graphics("images/chapter5/sdram_structure.png")
```

影响SDRAM芯片读写速度的因素有两个：行缓冲局部性（Row Buffer Locality，简称RBL）和Bank级并行度（Bank Level Parallelism，简称BLP）。

1）行缓冲局部性。如图\@ref(fig:sdram-structure)所示，SDRAM芯片的一行数据在从存储体中读出后，存储体中的值被破坏，保存在对应的一组感应放大器中，这组感应放大器也被称为行缓冲。如果下一个访存请求访问同一行的数据（称为命中行缓冲），可以直接从该感应放大器中读出，而不需要重新访问存储体内部，可以大大降低SDRAM的访问延迟。当然，在行缓冲不命中的时候，就需要首先将行缓冲中的数据写回存储体，再将下一行读出到行缓冲中进行访问。由此，对DRAM可以采用关行（Close Page）和开行（Open Page）两种策略。使用关行策略时，每次读写完后先把行缓冲的内容写入存储体，才能进行下一次读写，每次读写的延迟是确定的。使用开行策略时，每次读写完后不把行缓冲的内容写入存储体，如果下一次读写时所读写的数据在行缓冲中（称为行命中），可以直接对行缓冲进行读写即可，延迟最短；如果下一次读写时所读写的数据不在行缓冲中，则需要先将行缓冲中的数据写回对应的行，再将新地址的数据读入行缓冲，再进行读写，延迟最长。因此，如果内存访问的局部性好，可以采用开行策略；如果内存访问的局部性不好，则可以采用关行策略。内存控制器可以通过对多个访存请求进行调度，尽量把对同一行的访问组合在一起，以增加内存访问的局部性。

2）Bank级并行度。SDRAM芯片包含的多个Bank是相互独立的，它们可以同时执行不同的操作，比如，对Bank 0激活的同时，可以对Bank 1发出预充电操作，因此，访问不同Bank的多个操作可以并行执行。Bank级并行度可以降低冲突命令的等待时间，容忍单个Bank访问的延迟。

利用内存的这两个特性，可以在内存控制器上对并发访问进行调度，尽可能降低读写访问的平均延迟，提高内存的有效带宽。内存控制器可以对十几甚至几十个访存请求进行调度，有效并发的访存请求数越多，可用于调度的空间就越大，可能得到的访存性能就更优。

### 输入/输出设备

输入/输出设备（简称IO设备）实现计算机与外部世界的信息交换。传统的IO设备有键盘、鼠标、打印机和显示器等；新型的IO设备能进行语音、图像、影视的输入、输出和手写体文字输入，并支持计算机之间通过网络进行通信。磁盘等辅助存储器在计算机中也当作IO设备来管理。

处理器通过读写IO设备控制器中的寄存器来访问及控制IO设备。高速IO设备可以在处理器安排下直接与主存储器成批交换数据，称为直接存储器访问（Directly Memory Access，简称DMA）。处理器可以通过查询设备控制器状态与IO设备进行同步，也可以通过中断与IO设备进行同步。

下面以GPU、硬盘和闪存为例介绍典型的IO设备。

**1．GPU**

GPU（Graphics Processing Unit，图形处理单元）是与CPU联系最紧密的外设之一，主要用来处理2D和3D的图形、图像和视频，以支持基于视窗的操作系统、图形用户界面、视频游戏、可视化图像应用和视频播放等。

当我们在电脑上打开播放器观看电影时，GPU负责将压缩后的视频信息解码为原始数据，并通过显示控制器显示到屏幕上；当我们拖动鼠标移动一个程序窗口时，GPU负责计算移动过程中和移动后的图像内容；当我们玩游戏时，GPU负责计算并生成游戏画面。

GPU驱动提供OpenGL、DirectX等应用程序编程接口以方便图形编程。其中，OpenGL是一个用于3D图形编程的开放标准；DirectX是微软公司推出的一系列多媒体编程接口，包括用于3D图形的Direct3D。通过这些应用程序接口，软件人员可以很方便地实现功能强大的图形处理软件，而不必关心底层的硬件细节。

GPU最早是作为一个独立的板卡出现的，所以称为显卡。我们常说的独立显卡和集成显卡是指GPU是作为一个独立的芯片出现还是被集成在芯片组或处理器中。现代GPU内部包含了大量的计算单元，可编程性越来越强，除了用于图形图像处理外，也越来越多地用作高性能计算的加速部件，称为加速卡。

GPU与CPU之间存在大量的数据传输。CPU将需要显示的原始数据放在内存中，让GPU通过DMA的方式读取数据，经过解析和运算，将结果写至显存中，再由显示控制器读取显存中的数据并输出显示。将GPU与CPU集成至同一个处理器芯片时，CPU与GPU内存一致性维护的开销和数据传递的延迟都会大幅降低。此时系统内存需要承担显存的任务，访存压力也会大幅增加，因为图形应用具有天生的并行性，GPU可以轻松地耗尽有限的内存带宽。

GPU的作用是对图形API定义的流水线实现硬件加速，主要包括以下几个阶段：

- 顶点读入（Vertex Fetch）：从内存或显存中取出顶点信息，包括位置、颜色、纹理坐标、法向量等属性

- 顶点渲染（Vertex Shader）：对每一个顶点进行坐标和各种属性的计算

- 图元装配（Primitive Assembly）：将顶点组合成图元，如点、线段、三角形等

- 光栅化（Rasterization）：将矢量图形点阵化，得到被图元覆盖的像素点，并计算属性插值系数以及深度信息

- 像素渲染（Fragment Shader）：进行属性插值，计算每个像素的颜色

- 逐像素操作（Per-Fragment Operation）：进行模板测试、深度测试、颜色混合和逻辑操作等，并最终修改渲染缓冲区

在GPU中，集成了专用的硬件电路来实现特定功能，同时也集成了大量可编程的计算处理核心用于一些较为通用的功能实现。设计者根据每个功能使用的频率、方法以及性能要求，选择不同的实现方式。大部分GPU中，顶点读入、图元装配、光栅化及逐像素操作使用专用硬件电路实现，而顶点渲染和像素渲染采用可编程的计算处理核心实现。由于现代GPU中集成了大量可编程的计算处理核心，这种大规模并行的计算模式非常适合于科学计算应用，所以在高性能计算机领域，GPU常被用作计算加速单元配合CPU使用。

**2.硬盘**

计算机除了需要内存存放程序的中间数据外，还需要具有永久记忆功能的存储体来存放需要较长时间保存的信息。比如操作系统的内核代码、文件系统、应用程序和用户的文件数据等。该存储器除了容量必须足够大之外，价格还要足够便宜，同时速度还不能太慢。在计算机的发展历史上，磁性存储材料正好满足了以上要求。磁性材料具有断电记忆功能，可以长时间保存数据；磁性材料的存储密度高，可以搭建大容量存储系统；同时，磁性材料的成本很低。

人们目前使用的硬盘就是一种磁性存储介质。硬盘的构造原理为：将磁性材料覆盖在圆形碟片（或者说盘片）上，通过一个读写头（磁头）悬浮在碟片表面来感知存储的数据。通过碟片的旋转和磁头的径向移动来读写碟片上任意位置的数据。碟片被划分为多个环形的轨道（称为磁道，Track）来保存数据，每个磁道又被分为多个等密度（等密度数据）的弧形扇区（Sector）作为存储的基本单元。磁盘的内部构造如图\@ref(fig:disk-structure)所示。硬盘在工作时，盘片是一直旋转的，当想要读取某个扇区的数据时，首先要将读写头移动到该扇区所在的磁道上，当想要读写的扇区旋转到读写头下时，读写头开始读写数据。

```{r disk-structure, echo=FALSE, fig.align='center', fig.cap="磁盘的内部结构图", out.width='50%'}
knitr::include_graphics("images/chapter5/disk_structure.png")
```


衡量磁盘性能的指标包括响应时间和吞吐量，也就是延迟和带宽。磁头移动到目标磁道的时间称为寻道时间。当磁头移动到目标磁道后，需要等待目标扇区旋转到磁头下面，这段时间称为旋转时间。旋转时间与盘片的旋转速度有关，磁盘的旋转速度用RPM（Rotation Per Minute，转/分）来表示，我们常说的5400转、7200转，就是指磁盘的旋转速度。扇区旋转到目标位置后，传输这个扇区的数据同样需要时间，称为传输时间。传输时间是扇区大小、旋转速度和磁道记录密度的函数。

磁盘是由磁盘控制器控制的。磁盘控制器控制磁头的移动、接触和分离以及磁盘和内存之间的数据传输。另外，通过IO操作访问磁盘控制器又会引入新的时间。现在的磁盘内部一般都会包含一个数据缓冲，读写磁盘时，如果访问的数据正好在缓冲中命中，则不需要访问磁盘扇区。还有，当有多个命令读写磁盘时，还需要考虑排队延迟。因此，磁盘的访问速度计算起来相当复杂。一般来说，磁盘的平均存取时间在几个毫秒的量级。



磁盘的密度一直在持续增加，对于用户来说，磁盘的容量一直在不断增大。磁盘的尺寸也经历了一个不断缩小的过程，从最大的14英寸（1英寸=0.0254米）到最小的1.8英寸。目前市场上常见的磁盘尺寸包括应用于台式机的3.5英寸和应用于笔记本电脑的2.5英寸。

**3.闪存**

闪存（Flash Storage）是一种半导体存储器，它和磁盘一样是非易失性的存储器，但是它的访问延迟却只有磁盘的千分之一到百分之一，而且它尺寸小、功耗低，抗震性更好。常见的闪存有SD卡、U盘和SSD固态磁盘等。与磁盘相比，闪存的每GB价格较高，因此容量一般相对较小。目前闪存主要应用于移动设备中，如移动电话、数码相机、MP3播放器，主要原因在于它的体积较小。闪存在移动市场具有很强的应用需求，工业界投入了大量财力推动闪存技术的发展。随着技术的发展，闪存的价格在快速下降，容量在快速增加，因此SSD固态硬盘技术获得了快速发展。SSD固态硬盘是使用闪存构建的大容量存储设备，它模拟硬盘接口，可以直接通过硬盘的SATA总线与计算机相连。

最早出现的闪存被称为NOR型闪存，因为它的存储单元与一个标准的或非门很像。NAND型闪存采用另一种技术，它的存储密度更高，每GB的成本更低，因此NAND型闪存适合构建大容量的存储设备。前面所列的SD卡、U盘和SSD固态硬盘一般都是用NAND型闪存构建的。

使用闪存技术构建的永久存储器存在一个问题，即闪存的存储单元随着擦写次数的增多存在损坏的风险。为了解决这个问题，大多数NAND型闪存产品内部的控制器采用地址块重映射的方式来分布写操作，目的是将写次数多的地址转移到写次数少的块中。该技术被称为磨损均衡（Wear Leveling）。闪存的平均擦写次数在10万次左右。这样，通过磨损均衡技术，移动电话、数码相机、MP3播放器等消费类产品在使用周期内就不太可能达到闪存的写次数限制。闪存产品内部的控制器还能屏蔽制造过程中损坏的块，从而提高产品的良率。

## 计算机系统硬件结构发展

前面章节从冯·诺依曼结构出发，介绍了现代计算机的理论结构及其组成部分。随着应用需求的变化和工艺水平的不断提升，冯·诺依曼结构中的控制器和运算器逐渐演变为计算机系统中的中央处理器部分，而输入、输出设备统一通过北桥和南桥与中央处理器连接，中央处理器中的图形处理功能则从中央处理器中分化出来形成专用的图形处理器。因此，现代计算机系统的硬件结构主要包括了中央处理器、图形处理器、北桥及南桥等部分。

**中央处理器**（Central Processing Unit，简称CPU）主要包含控制器和运算器，在发展的过程中不断与其他部分融合。传统意义上的中央处理器在处理器芯片中更多地体现为处理器核，现代的处理器芯片上往往集成多个处理器核。

**图形处理器**（Graphic Processing Unit，简称GPU）是一种面向2D和3D图形、视频、可视化计算和显示优化的处理器。作为人机交互的重要界面，GPU在计算机体系结构发展的过程中，担任了越来越重要的角色。除了对图形处理本身之外，还开始担负科学计算加速器的任务。

**北桥**（North Bridge）是离CPU最近的芯片，主要负责控制显卡、内存与CPU之间的数据交换，向上连接处理器，向下连接南桥。

**南桥**（South Bridge）主要负责硬盘、键盘以及各种对带宽要求较低的IO接口与内存、CPU之间的数据交换。

### CPU-GPU-北桥-南桥四片结构

现代计算机的一种早期结构是CPU-GPU-北桥-南桥结构。在该结构中，计算机系统包含四个主要芯片，其中CPU（处理器）芯片、北桥芯片和南桥芯片一般是直接以芯片的形式安装或焊接在计算机主板上，而GPU则以显卡的形式安装在计算机主板的插槽上。

在CPU-GPU-北桥-南桥四片结构中，计算机的各个部件根据速度快慢以及与处理器交换数据的频繁程度被安排在北桥和南桥中。CPU通过处理器总线（也称系统总线）和北桥直接相连，北桥再通过南北桥总线和南桥相连，GPU一般以显卡的形式连接北桥。内存控制器集成在北桥芯片中，硬盘接口、USB接口、网络接口、音频接口以及鼠标、键盘等接口放在南桥芯片中。此外，在北桥上还会提供各种扩展接口用于其他功能卡的连接。采用该结构的微机系统如图\@ref(fig:structure-4part)所示。

```{r structure-4part, echo=FALSE, fig.align='center', fig.cap="CPU-GPU-北桥-南桥结构", out.width='50%'}
knitr::include_graphics("images/chapter5/structure_4part.png")
```


与英特尔奔腾处理器搭配的430HX芯片组就采用了这样的四片结构。其北桥芯片使用82439HX，南桥芯片采用82371SB，通过PCI总线扩展外接显卡，与处理器组成四片结构，作为计算机系统的主要部分。

### CPU-北桥-南桥三片结构

现代计算机的一种典型结构是CPU-北桥-南桥结构。在该结构中，系统包含三个主要芯片，分别为CPU芯片、北桥芯片和南桥芯片。三片结构与四片结构最大的区别是，前者GPU功能被集成到北桥，即一般所说的集成显卡。

在CPU-北桥-南桥三片结构中，CPU通过处理器总线和北桥直接相连，北桥再通过南北桥总线和南桥相连。内存控制器、显示功能以及高速IO接口（如PCIE等）集成在北桥芯片中，硬盘接口、USB接口、网络接口、音频接口以及鼠标、键盘等接口部件放在南桥芯片中。随着计算机技术的发展，更多的高速接口被引入计算机体系结构中，在北桥上集成的IO接口的速率也不断提升。

采用该结构的微机系统如图\@ref(fig:structure-3part)所示。

```{r structure-3part, echo=FALSE, fig.align='center', fig.cap="CPU -北桥 -南桥结构", out.width='50%'}
knitr::include_graphics("images/chapter5/structure_3part.png")
```

英特尔845G芯片组就采用类似的三片结构。其北桥芯片使用82845G，集成显示接口，南桥芯片采用82801DB，与处理器组成三片结构，作为计算机系统的主要部分。

### CPU-弱北桥-南桥三片结构

随着工艺和设计水平的不断提高，芯片的集成度不断提高，单一芯片中能够实现的功能越来越复杂。内存接口的带宽需求超过了处理器与北桥之间连接的处理器总线接口，导致内存的实际访问性能受限于处理器总线的性能。而伴随着处理器核计算性能的大幅提升，存储器的性能提升却显得幅度较小，这两者的差异导致计算机系统性能受到存储器系统发展的制约，这就是存储墙问题。

因此，对计算机系统性能影响显著的内存控制器开始被集成到CPU芯片中，从而大幅降低了内存访问延迟，提升了内存访问带宽，这在一定程度上缓解了存储墙问题。

于是，北桥的功能被弱化，主要集成了GPU、显示接口、高速IO接口（例如PCIE接口等）。

采用该结构的微机系统如图\@ref(fig:structure-3part-weaknb)所示。

```{r structure-3part-weaknb, echo=FALSE, fig.align='center', fig.cap="CPU -弱北桥 -南桥结构", out.width='50%'}
knitr::include_graphics("images/chapter5/structure_3part_weaknb.png")
```

相比英特尔，AMD的处理器最早将内存控制器集成到处理器芯片中，780E芯片组就采用上述三片结构，北桥芯片使用RS780E，集成HD3200 GPU，南桥芯片使用SB710，与处理器组成三片结构，作为计算机系统的主要部分。

### CPU-南桥两片结构

在计算机系统不断发展的过程中，图形处理器性能也在飞速发展，其在系统中的作用也不断被开发出来。除了图形加速以外，对于一些科学计算类的应用，或者是一些特定的算法加速程序，图形处理器发挥着越来越大的作用，成为特定的运算加速器，其与中央处理器之间的数据共享也越来越频繁，联系越来越密切。

随着芯片集成度的进一步提高，图形处理器也开始被集成到CPU芯片中，于是，北桥存在的必要性就进一步降低，开始和南桥合二为一，形成CPU-南桥结构，如图\@ref(fig:structure-2part)所示。

在这个结构中，CPU芯片集成处理器核、内存控制器和GPU等主要部件，对外提供显示接口、内存接口等，并通过处理器总线和南桥相连。南桥芯片则包含硬盘、USB、网络控制器以及PCIE/PCI、LPC等总线接口。由于GPU和CPU都需要大量访问内存，会带来一些访存冲突，而且相对来说，GPU对于实时性的要求更高，即访存优先级会更高一些，这在一定程度上会影响CPU的性能。实际上，处理器中集成的GPU性能相比独立显卡中的GPU性能会稍弱。

当然，也有一些两片结构是将GPU集成在南桥芯片中。这样在南桥上可以实现独立的显存供GPU使用，这在某些条件下更有利于GPU性能的发挥，且CPU升级时带来的开销会更小。

```{r structure-2part, echo=FALSE, fig.align='center', fig.cap="CPU - 南桥结构", out.width='50%'}
knitr::include_graphics("images/chapter5/structure_2part.png")
```
### SoC单片结构

片上系统（System on Chip，简称SoC）是一种单片计算机系统解决方案，它在单个芯片上集成了处理器、内存控制器、GPU以及硬盘、USB、网络等IO接口，使得用户搭建计算机系统时只需要使用单个主要芯片即可，如图\@ref(fig:soc)所示。

```{r soc, echo=FALSE, fig.align='center', fig.cap="SOC单片结构", out.width='50%'}
knitr::include_graphics("images/chapter5/soc.png")
```

目前SoC主要应用于移动处理器和工业控制领域，相比上述几种多片结构，单片SoC结构的集成度更高，功耗控制方法更加灵活，有利于系统的小型化和低功耗设计。但也因为全系统都在一个芯片上实现，导致系统的扩展性没有多片结构好，升级的开销也更大。随着技术的发展，封装基板上的互连技术不断发展和成熟。越来越多的处理器利用多片封装技术在单个芯片上集成多个硅片，以扩展芯片的计算能力或IO能力。例如AMD Ryzen系列处理器通过在封装上集成多个处理器硅片和IO硅片，以提供针对不同应用领域的计算能力。龙芯3C5000L处理器则通过在封装上集成4个4核龙芯3A5000硅片来实现单片16核结构。

目前，主流商用处理器中面向中高端领域的处理器普遍采用两片结构，而面向中低端及嵌入式领域的处理器普遍采用单片结构。SoC单片结构最常见的是在手机等移动设备中。

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

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

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

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

### IO寄存器寻址

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

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

### 处理器和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寄存器进行处理。

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

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

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

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

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

在PIO方式中，数据要经过处理器内部的通用寄存器进行中转。中转不仅影响处理器的执行，也降低了数据传送的速率。如果在存储器和IO设备之间开辟一条数据通道，专门用于数据传输，就可以将处理器从数据搬运中解放出来。这种方式就是DMA（Direct Memory Access，直接存储器访问）方式。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控制器用于自身的数据传输。



表\@ref(tab:pio-vs-dma)举例说明了PIO和DMA两种数据传输方式的不同。

```{r pio-vs-dma, echo = FALSE, message=FALSE, tab.cap='PIO和DMA两种数据传输方式'}
autonum <- run_autonum(seq_id = "tab", bkm = "pio-vs-dma", bkm_all = TRUE)
readr::read_csv('./materials/chapter5/pio_vs_dma.csv') %>%
flextable() %>%
set_caption(caption="PIO和DMA两种数据传输方式", autonum = autonum) %>%
width(j=1, width=2.5) %>%
width(j=2, width=3.5) %>%
theme_box()
```

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

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

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

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

```{r loongson-3A3000, echo=FALSE, fig.align='center', fig.cap="龙芯3A3000+7A1000两片方案", out.width='50%'}
knitr::include_graphics("images/chapter5/loongson_3A3000.png")
```

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

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

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

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

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

```{r 3A3000-display, echo=FALSE, fig.align='center', fig.cap="3A3000+7A1000的不同显示方式", out.width='100%'}
knitr::include_graphics("images/chapter5/3A3000_display.png")
```

## 本章小结

本章介绍了计算机系统的基本原理和硬件组成结构。冯·诺依曼结构将计算机分为控制器、运算器、存储器、输入设备和输出设备五大部分，这一章重点介绍了冯·诺依曼结构组成部分的结构及各部分之间的关系，尤其是CPU、内存、IO之间的相互关系。

## 习题

1.	分别说明图\@ref(fig:structure-4part) – \@ref(fig:structure-2part)中四种结构中每个芯片包含冯·诺依曼结构五个部分的哪部分功能。
2.	查阅资料，比较Skylake处理器和Zen处理器的运算器结构。
3.	说明ROB、保留站（发射队列）、重命名寄存器在指令流水线中的作用，并查阅资料，比较Skylake处理器和Zen处理器的ROB、发射队列、重命名寄存器项数。


4.	假设A处理器有两级Cache，一级Cache大小为32KB，命中率为95%，命中延迟为1拍；二级Cache大小为1MB，命中率为80%，命中延迟为30拍，失效延迟为150拍。B处理器有三级Cache，一级Cache大小为32KB，命中率为95%，命中延迟为1拍；二级Cache大小为256KB，命中率为75%，命中延迟为20拍；三级Cache大小为4MB，命中率为80%，命中延迟为50拍，失效延迟为150拍。比较两款处理器的平均访问延迟。
5.	假设某内存访问，行关闭、打开、读写各需要两拍，在行缓存命中率为70%和30%的情况下，采用open page模式还是close page模式性能更高？

6.	简要说明处理器和IO设备之间的两种通信方式的通信过程。
7.	简要说明处理器和IO设备之间的两种同步方式的同步过程。
8.	在一个两片系统中，CPU含内存控制器，桥片含GPU、DC和显存，简要说明在PPT翻页过程中，CPU、GPU、DC、显存、内存之间的同步和通信过程。
9.	调查目前市场主流光盘、硬盘、SSD盘、内存的价格，并计算每GB存储容量的价格。

\newpage
