Skip to content

Ch01 计算机系统概述

这部分内容大多在 DLCO 课程上学过,故而会比较一笔带过。

计算机系统的基本工作原理

冯诺依曼结构的基本思想

世界上第一台真正意义上的电子数字计算机是 1935-1939 年间由阿塔纳索夫(John Vincent Atanasoff)和其合作者贝瑞(Clifford Berry)使用 300 个电子管研制成功的 ABC(Atanasoff-Berry Computer),不过这只是一个样机。

1946 年 2 月,莫克利(John W. Mauchly)和艾克特(John Presper Eckert)作为负责人,真正实用的电子数字计算机 ENIAC(Electronical Numerical Integrator and Computer)被研制出来,其设计思想基本来源于 ABC,且和 ABC 有专利之争。

现在国际计算机界公认阿塔纳索夫是第一台电子计算机的真正发明人,被称为「电子计算机之父」。

冯诺依曼(John von Neumann)在研制 ENIAC 的同时,考虑研制另一台电子计算机 EDVAC(Electronic Discrete Variable Automatic Computer)。1945 年,他以「关于 EDVAC 的报告草案」为题,起草了一篇报告,发表了全新的存储程序(stored-program)通用电子计算机方案,宣告了现代计算机结构,即「冯诺依曼结构」的诞生。

存储程序方式的基本思想是:必须将事先编好的程序和原始数据送入主存后彩能执行程序,一旦程序被启动执行,计算机能在不需操作人员干预下自动完成逐条指令取出和执行的任务。

冯诺依曼结构的基本思想主要包括以下几个方面:

  • 采用「存储程序」工作方式。
  • 计算机由「运算器」、「控制器」、「存储器」、「输入设备」和「输出设备」五个基本部分组成。
  • 存储器不仅嗯那个存放数据,也能存放指令,数据和指令在形式上没有区别,但计算机能区分它们;控制器应能自动执行指令;运算器应能进行算术运算,也能进行逻辑运算;操作人员可以通过输入设备和输出设备使用计算机。
  • 计算机内部以二进制形式表示指令和数据;每条指令由操作码和地址码两部分组成,操作码指出操作类型,地址码指出操作数的地址;由一串指令组成程序。

冯诺依曼机的基本结构

根据冯诺依曼结构的基本思想,可以给出一个模型计算机的基本硬件结构。

【这里有一张图】

模型机中主要包括:

  • 「主存」或「内存」:用来存放指令和数据的主存储器。

  • 「算术逻辑部件」(ALU, Arithmetic Logic Unit):用于进行算术逻辑运算的运算器。其在 ALU 操作控制信号 ALUop 的控制下,ALU 可以对输入端 A 和 B 进行不同的运算,得到结果 F.

  • 「控制部件」或「控制器」(CU, Control Unit):用于自动逐条取出指令并进行译码的部件。
  • 「输入设备」和「输出设备」:用于用户交互。

为了更好的实现其结构,我们引入以下部件:

  • 「通用寄存器组」(GPRs, General Purpose Registers):用于临时存放从主存取来的数据或运算的结果。ALU 两个输入端的数据来自通用寄存器。
  • 「标志寄存器」:用于存放 ALU 运算结果产生的标志信息。
  • 「指令寄存器」(IR, Instruction Register):用于临时保存从主存取来的指令。
  • 「程序计数器」(PC, Program Counter):CPU 为了自动按序读取主存中的指令,在执行当前指令的过程中,自动计算出下一条指令的地址并送 PC 保存。
  • 「中央处理器」(CPU, Central Processing Unit):通常把控制器、运算器和各类寄存器互连组成的电路称为 CPU.

CPU 需要从通用寄存器中取数据到 ALU 中进行计算,或把 ALU 计算的结果保存到通用寄存器中,因此需要给每个通用寄存器编号;

主存中每个单元也需要编号,称为「主存单元地址」,简称「主存地址」。

通用寄存器和主存都属于存储部件,计算机中的存储部件从 0 开始编号。

为了从主存取指令和数据,CPU 需要通过传输介质和主存相连。通常把连接不同部件进行信息传输的介质称为「总线」,其中包含用于传输地址信息、数据信息和控制信息的「地址线」、「数据线」和「控制线」。

CPU 访问主存时,需先将主存地址、读写命令分别发送到总线的地址线、控制线,然后通过数据线发送或接收数据。

CPU发送到地址线的主存地址应先存放在「主存地址寄存器」(MAR, Memory Address Register)中,发送到数据线或从数据线获取的信息存放在「主存数据寄存器」(MDR, Memory Data Register)中。

程序和指令的执行过程

程序的执行过程就是所包含的指令的执行过程。

指令(instruction)是一串 0/1 序列,用于指示 CPU 完成一个特定的原子操作。

指令通常被划分为若干个字段,包含操作码、地址码等。「操作码字段」指出指令的操作类型,「地址码字段」指出指令所处理的操作数的地址。

程序执行过程,对指令执行经过这些阶段:

  • 根据 PC 取指令
  • 指令译码,更新 PC
  • 取操作数并执行
  • 送结果

一般来说会拆解为这五个阶段:取指(IF)、译码(ID)、执行(EX)、访存(MEM)、写回(WB)。

指令执行各阶段都会包含若干个「微操作」,其需要相应的控制信号(control signal)进行控制。

通常,CPU 中所有微操作都由始终信号进行定时。「时钟信号」(clock signal)的宽度为一个「时钟周期」(clock cycle),一条指令的执行时间包含一个或多个时钟周期。

程序的开发与运行

首先,应将应用问题(任务)转化为「算法」(algorithm)描述,使应用问题的求解变为流程化的清晰步骤,并保证步骤有限。任何一个问题都可能有多个求解算法,需要进行算法分析以确定算法的,特别是在时间和空间上的,高效性。

其次,将算法转换为用编程语言描述的程序,这个转换通常是由程序员进行程序设计、手工进行的。「程序设计语言」(programming language)与自然语言不同,其有严格的执行顺序,不存在二义性,从而保证程序行为与算法描述一致。

程序设计语言和翻译程序

程序设计语言可以分成不同抽象层的、适用于不同领域的、采用不同描述结构的等等。从抽象层次上来分,可以分为「高级语言」和「低级语言」两类。

使用特定计算机规定的指令格式而形成的 01 序列称为「机器语言」,使用其写成的计算机能理解和执行的程序称为「机器代码」或「机器语言程序」,其中的每条指令都由 01 组成,称为「机器指令」。

最早人们采用机器语言编写程序,但可读性太差,给程序的编写和阅读带来了极大困难。人们于是引入了一种机器语言的符号表示语言,其通过用简短的英文符号和机器指令建立对应关系,以方便程序员编写和阅读程序,这种语言就被称作「汇编语言」(assembly language),机器指令对应的符号表示称为「汇编指令」。

但是计算机无法理解汇编语言,所以「汇编语言源程序」需要转换为机器语言程序才能被执行。汇编指令和对应的机器指令一一对应,且都和特定的机器结构相关,二者统称为「机器级语言」(machine level language)。

因机器级语言每条指令功能很简单,描述程序功能时细节太多,为此发明了「高级程序设计语言」(high level programming language),又称「高级编程语言」,其是指面向算法设计的,较接近于日常英语书面语言的程序设计语言。

显然,机器也无法直接理解和执行高级编程语言程序,故需要「翻译程序」或「翻译器」(translator)将其转换为机器语言程序,被翻译的语言和程序分别称为「源语言」和「源程序」,翻译生成的语言和程序分别称为「目标语言」和「目标程序」,翻译程序一般有以下三类:

  • 「汇编程序」或「汇编器」(assembler):将汇编语言源程序翻译为机器语言目标程序。
  • 「解释程序」或「解释器」(interpreter):将源程序中的语句按执行顺序逐条翻译为机器指令并立即执行。
  • 「编译程序」或「编译器」(compiler):将高级语言源程序翻译成汇编语言或机器语言目标程序。

从源程序到可执行文件

以 C 语言源程序(文本文件) hello.c开始,逐步转换为可执行目标文件 hello 的过程如下:

  1. 经过预处理程序(cpp)转换为预处理过的源文件(文本文件)hello.i.
  2. 经过编译程序(ccl)转换为汇编源程序(文本文件)hello.s.
  3. 经过汇编程序(as)转换为可重定位目标程序(二进制文件)hello.o
  4. 经过链接程序(ld)转换为可执行目标程序(二进制文件)hello

预处理阶段:预处理程序对源程序中以 # 字符开头的命令进行处理,预处理程序的输出结果还是一个源程序文件,但以 .i 为扩展名。

编译阶段:编译程序对预处理后的源程序进行编译,生成一个汇编语言源程序文件,以 .s 为扩展名。其和具体的机器结构有关。

汇编阶段:汇编程序对汇编语言源程序进行汇编,生成一个「可重定位目标文件」(relocatable object file),以 .o 为扩展名,其为二进制文件,打开后为乱码而不可读。

链接阶段:链接程序将多个可重定位目标文件和标准函数库中的可重定位目标文件合并成为「可执行目标文件」(executable object file),简称为可执行文件。

具体地,在 Linux 系统中,可使用如下命令获得其可执行文件:

linux> gcc -o hello hello.c

可执行文件的启动和执行

在 Linux 系统中可以通过 shell 命令行解释器来执行可执行文件,例如:

linux> ./hello

其中 ./hello 表示了 hello 文件的路径,具体来说,shell 程序会将用户从键盘输入的每个字符逐一读入 CPU 寄存器中,然后将其保存到主储存器中,在主存的缓冲区中形成输入的字符串。一行输入完了,shell 将调出操作系统给内核中响应的服务例程,由内核来加载硬盘上的可执行文件到存储器。内核加载完可执行文件中的代码及其索要处理的数据后,将文件第一条指令的地址送到程序计数器 PC 中,因 CPU 永远都将 PC 中的内容作为将要执行的指令的地址,因此处理器随后开始执行程序,最终将结果从主存取到 CPU 的寄存器中,并送显示器显式。

【这里有一张图】

在这个过程中,用户程序被启动执行,必须依靠操作系统的支持,其包括人机接口环境(如外壳 shell 程序)和内核 kernel 服务例程。

其中 shell 命令行解释器就是操作系统外壳程序,其为用户提供了一个启动程序执行的环境,用于对用户从键盘输入的命令进行解释,并调出操作系统内核来加载用户程序(用户从键盘输入的命令所对应的程序)。

其中内核服务例程用于加载用户程序并使其从第一条指令开始执行。

此外,对于键盘、硬盘和显示器等外部设备的操作,因这些底层硬件不能由用户程序直接访问,故而也需要依靠操作系统内核服务例程的支持。

这些外部设备简称为「外设」,也称为「I/O 设备」,I/O 即 Input/Output(输入输出)。外部设备通常由机械部分和电子部分组成,两部分通常是分开的。机械部分是外设本身,电子部分是控制外设工作的「I/O 控制器」或「I/O 适配器」,其中外设通过 I/O 控制器或 I/O 适配器连到主机,I/O 控制器和 I/O 适配器统称「设备控制器」。例如键盘接口、打印机适配器、显示控制卡(显卡)、网络控制卡(网卡)等都是设备控制器,属于「I/O 模块」。

程序的执行过程就是数据在 CPU、主存和 I/O 模块之间流动的过程,所有数据的流动都是通过「总线」、「I/O 桥接器」等进行的,在总线上传输数据之前,需要先将其缓存在存储部件中。除主存本身是存储部件外,在 CPU、I/O 桥接器、设备控制器中也有存放数据的缓冲存储部件。

计算机系统的层次结构

传统计算机系统采用分层方式构建,即计算机系统是一个层次结构,通过向上层用户提供一个抽象的简洁接口而将较低层次的实现细节隐藏起来,计算机解决应用问题的过程就是不同抽象层进行转换的过程。

计算机系统抽象层的转换

1. 应用(问题)
2. 算法
3. 编程(语言)
4. 操作系统 / 虚拟机
5. 指令集体系结构(ISA)
6. 微体系结构
7. 功能部件 / RTL
8. 电路
9. 器件

其中:1.-5. 为软件层,5.-9. 为硬件层。1. 面向最终用户,2.-4. 面向程序员,5.-7. 面向架构师,8.-9. 面向电子工程师。

希望计算机完成或解决的任何应用(问题)最开始形成时通常用自然语言描述,但是计算机只能理解机器语言。
要将一个自然语言描述的应用问题转换为机器语言程序,需要经过应用问题描述、算法抽象、高级语言程序设计、将高级语言源程序转换为特定机器语言目标程序等多个抽象层的转换。
在进行高级语言程序设计时,需要有相应的「程序开发支撑环境」。需要一个「程序编辑器」,以方便源程序的编写,需要一套「翻译转换软件」,以处理各类源程序,其中包括预处理程序、编译器、汇编器、链接器等;还需要一个可以执行各类程序的「用户界面」,如 GUI 方式下的「图形用户界面」或 CLI 方式下的「命令行用户界面」(如 shell 程序)。
提供程序编辑器和各类翻译转换软件的工具包统称为「语言处理系统」,而具有人机交互功能的用户界面和底层系统调用服务例程则由「操作系统」提供。
所有的语言处理系统都必须在操作系统提供的计算机环境中运行,操作系统是对计算机底层结构和计算机硬件的一种抽象,这种抽象构成了可以让程序员使用的「虚拟机」(virtual machine).
机器语言程序所运行的计算机硬件和软件之间的所谓桥梁,这个界面就是「指令集体系结构」(ISA, instructioni set architecture),简称「指令系统」,也称为「架构」,它是软件和硬件之间接口的一个完整定义。
ISA 定义了一台计算机可以执行的所有指令的集合,每条指令规定了计算机执行什么操作,以及所处理的操作数存放的地址空间和操作数类型,ISA 规定的内容包括:数据类型及格式,指令格式,寻址方式和可访问地址空间大小,程序可访问的通用寄存器的个数、位数和编号,状态/控制寄存器的定义,I/O 空间的编址方式,终端结构,机器工作状态的定义和转换,I/O 传送方式,存储保护方式等。因此,指令集体系结构是指软件能感知到的部分,也称「软件可见部分」。
机器语言程序就是一个 ISA 规定的指令序列,因此,计算机硬件执行机器语言程序的过程就是执行一条条指令的过程。ISA 是对指令系统的一种规定或结构规范,实现 ISA 的具体逻辑结构称为「计算机组织」(computer organization)或「微体系结构」(micro-architecture),简称为「微架构」。
ISA 和微体系结构是不同层面上的两个概念,微体系结构是软件不可感知的部分。例如加法器采用串行进位还是并行进位实现属于微体系结构考虑的范畴,与程序编写无关,ISA 也不知道这里加法具体的实现。
相同的 ISA 可能具有不同的微体系结构。微体系结构最终由「逻辑电路」(logic circuit)实现,而每个基本的逻辑电路都是按照特定的「器件技术」(device technology)实现的。

计算机核心层之间的关联

高级编程语言编译器将高级语言源程序转换为机器级目标代码,这个过程中包含多个步骤,包括词法分析、语法分析、语义分析、中间代码生成、代码优化、目标代码生成和目标代码优化等。这整个过程可以划分为「前端」和「后端」两块,通常把中间代码生成及之前的部分称为前端。

因此,前端主要完成对源程序的分析,把源程序切分成一些基本块,并生成中间语言表示,后端在分析结果正确无误的基础上,把中间语言表示(中间代码)转化为目标机器支持的机器级语言程序。

每一种程序设计语言都有相应的标准规范。一方面,编译器开发者必须按照编程语言标准规范设计编译器前端,彩能向高级语言程序员提供可正确工作的编译器;另一方面,程序员必须按照语言标准规范编写源程序,源程序才能被编译器正确处理,否则编译过程出错或编译出的目标程序运行结果不符预期。

语言标准规范中有以下三种行为需要注意:

  • 「未定义行为」(undefined behavior):符合语言标准规范但未明确指定其结果的行为。若源程序包含未定义行为,则目标程序的每次运行结果可能不同,或在不同平台下运行结果不同。
  • 「未指定行为」(unspecified behavior):语言标准规范列出多种供编译器选择的行为结果的行为,不同编译器可能选择不同的行为结果。若源程序包含未指定行为,则同一份程序采用不同编译器或一款编译器的不同版本,可能给出不同的运行结果。
  • 「实现定义行为」(implementation-defined behavior):指语言标准规范的实现需要在文档中说明其选择的未指定行为。若源程序包含实现定义行为,在相同环境下运行可得到相同的结果,但在其他环境时运行结果可能不同。

编译器后端的设计应遵循 ISA 规范和「应用程序二进制接口」(ABI, application binary interface)规范。

ABI 是为运行在特定 ISA 及特定操作系统上的应用程序规定的一种机器级目标代码层接口,包含了运行在特定 ISA 及特定操作系统上的应用程序所对应的目标代码生成时必须遵循的约定。ABI 描述了应用程序和操作系统之间、应用程序和所调用的库函数之间、不同组成部分(如过程或函数)之间在较低层次上的机器级代码接口。

例如过程之间的调用约定(参数和返回值如何传递等)、系统调用约定(系统调用的参数和调用号如何传递,以及如何从用户态陷入操作系统内核等)、目标文件的二进制格式和函数库使用约定、机器中寄存器的使用规定、程序的虚拟地址空间划分等。

不符合 ABI 规范的目标程序将无法在根据该 ABI 规范提供的操作系统运行环境中正确运行。

ABI 和「应用程序接口」(API, application program interface)不同。API 定义了较高层次的源程序代码和库之间的接口,此接口通常与硬件无关。因此,同样的源程序代码可以在支持相同 API 的任意系统中进行编译以生成目标代码。在 ABI 相同或兼容的系统上,一个已经编译好的目标代码则可以无须改动而直接运行。

故而,在 ISA 层之上,操作系统向应用程序提供的运行时环境需要符合 ABI 规范,同时操作系统也要根据 ISA 规范来使用硬件提供的接口。包括硬件提供的各种控制寄存器和状态寄存器、原子操作、中断机制、分段和分页存储管理部件等。

在 ISA 层之下,设计处理器时需要根据 ISA 规范来设计相应的硬件接口供操作系统和应用程序使用。

总之,计算机系统能够按照与其正确地工作是不同层次的多个规范相互支撑的结果,计算机系统的各抽象层之间如何进行转换,最终都是由这些规范来定义的。不管是系统软件开发者、应用程序开发者还是处理器设计者,都必须以规范为准绳,应以手册为准。如果要了解程序的确切行为,最好的、最精确的方法就是查手册。

本书所用平台为 IA32/x86-64 + Linux + GCC + C 语言,Linux 操作系统下一般使用 system V ABI,因此推荐查询:C 语言标准、system V ABI 手册、Intel 指令系统手册

计算机系统的不同用户

计算机系统完成的所有任务都是通过执行程序中的指令来实现的。
计算机系统由硬件和软件两部分组成。「硬件」(hardware)是物理装置的总成,「软件」(software)包括运行在硬件上的程序和数据以及相关的文档。
「程序」(program)是指挥计算机如何操作的一个指令序列。
「数据」(data)是指令操作系统的对象。
根据软件的用途,一般将软件分成系统软件和应用软件两大类。
「系统软件」(system software)包括为有效、安全地使用和管理计算机一级为开发和运行应用软件而提供的各种软件,介于计算机硬件和应用程序之间,它与具体应用的关系不大。
系统软件包括操作系统(如 Windows, Unix, Linux)、语言处理系统(如 Visual Studio, GCC)、数据库管理系统(如 Oracle)和各类实用程序(如磁盘碎片整理程序、备份程序)。
「操作系统」(OS, operating system)主要用来管理整个计算机系统的资源,包括对它们进行调度、管理、监视和服务等。操作系统还提供计算机用户和硬件之间的人机交互界面,并提供对应用软件的支持。
语言处理系统主要用于提供一个用高级语言编程的环境,包括源程序编辑、翻译、调试、链接、装入、运行等功能。
「应用软件」(application software)指专门为数据处理、科学计算、事务管理、多媒体处理、工程设计以及过程控制等应用所编写的各类程序。

按照在计算机上完成任务的不同,可以把计算机的用户分为以下四类:

  • 「最终用户」(end user):大多数计算机使用者。
  • 「系统管理员」(system administrator):利用操作系统、数据库管理系统等软件提供的功能对系统进行配置、管理和维护,以建立高效、合理的系统环境供计算机用户使用的操作人员。 其职责主要包括:安装、配置和维护系统的硬件和软件,建立和管理用户账户,升级软件,备份、恢复业务系统和数据等。
  • 「应用程序员」(application programmer):使用高级编程语言编制应用软件的程序员。
  • 「系统程序员」(system programmer):设计和开发系统软件的程序员。
计算机最终用户通过键盘和鼠标等外设与计算机交互,通过操作系统提供的用户界面启动并执行应用程序或系统命令,从而完成用户任务。
因此,最终用户能够感知到的只是系统提供的简单人机交互页面和安装在计算机中的相关应用程序。

系统管理员作为管理和维护计算机系统的专业人员,必须能够安装、配置和维护系统的硬件和软件,能过建立和管理用户账户,需要时能过升级硬件和软件以及备份、恢复业务系统和数据等。系统管理员应该非常熟悉操作系统提供的有关系统配置和管理方面的功能。
因此,系统管理员能感知到的是系统中的部分硬件层面,系统管理层面以及相关实用程序和人机交互界面。

应用程序员大多使用高级程序设计语言编写程序。应用程序员所看到的计算机系统出了计算机硬件、操作系统提供的应用编程接口、人机交互界面和实用程序外,还包括相应的程序语言处理系统。
在程序语言处理系统中,出了翻译程序外,通常还包括编辑程序、链接程序、装入程序以及将这些程序和工具继承在一起而构成的「集成开发环境」(IDE, integrated development environment)等。此外,语言处理系统中还包括可供应用程序调用的各类函数库。

系统程序员在开发操作系统、编译器和使用程序等系统软件时,需要熟悉计算机底层的相关硬件和系统结构,甚至可能需要直接与计算机硬件和指令系统打交道,比如直接对各种控制寄存器、用户可见寄存器,I/O 控制器等硬件进行控制和编程。
因此,系统程序员不仅要熟悉应用程序员所使用的所有语言和工具,还必须熟悉指令系统、机器结构和相关的机器功能特性,有时还要直接使用汇编语言等低级语言编写程序代码。

可以认为计算机系统是由各种硬件和各类软件采用层次化方式构建的分层系统,不同计算机用户工作所在的系统结构层如下图所示。

【这里有一张图】

系统程序员所看到的机器属性属于 ISA 层面的内容,所看到的机器是配置了指令系统的机器,称为「机器语言机器」,工作在该层次的程序员称为「机器语言程序员」。

系统管理员工作在操作系统层,所看到的是配置了操作系统的虚拟机器,称为「操作系统虚拟机」。

汇编语言程序员工作在提供汇编程序的虚拟机器级,所看到的机器称为「汇编语言虚拟机」。

应用程序员大多工作在提供编译器或解释器等翻译程序的语言处理系统层,因此,应用程序员大多用高级语言编写程序,因而也称为「高级语言程序员」,所看到的虚拟机器称为「高级语言虚拟机」。

最终用户则工作在最上面的应用程序层。

章节小结

计算机在控制器的控制下完成数据处理、数据存储和数据传输三个基本功能,因而它由完成相应功能的控制器、运算器、存储器、输入/输出设备组成。在计算机内部,指令和数据都用二进制表示,两者在形式上没有任何差别,都是 0/1 序列,都存放在存储器中,按地址访问。计算机采用「存储程序」方式进行工作。指令格式中包含操作码字段和地址码字段等,地址吗可以是主存单元号,也可以是通用寄存器编号,用于指出操作数所在的主存单元或通用寄存器。

计算机系统以逐层向上抽象的方式构成,通过向上层用户提供一个抽象的简洁接口将较低层次的实现细节隐藏起来。系统中软件和硬件之间的抽象层就是「指令集体系结构」(ISA)。硬件和软件相辅相成,缺一不可,两者都可用来实现逻辑功能。

计算机完成一个任务的大致过程如下:用某种程序设计语言编制源程序,用语言处理程序将源程序翻译成机器语言目标程序,将目标程序中的指令和数据装入内存,然后从第一条指令开始执行,知道程序中的指令全部执行完。每条指令的执行包括取指令、指令译码、PC 增量、去操作数、运算、送结果等操作。