读书笔记,如需转载,请注明作者:Yuloran (t.cn/EGU6c76)
前言
计算机组成原理简介,主要摘选自唐朔飞编著的《计算机组成原理》第二版。
高级语言是如何被计算机执行的?
最初的计算机并没有微指令系统。由于 M0、M1 都是实际存在的,为了区分,这里分为微程序机器、传统机器。
将高级语言翻译成机器语言的程序叫做翻译程序,翻译程序分为编译程序与解释程序两种类型:
- 编译程序:一次性将高级语言全部翻译成机器语言
- 解释程序:翻译一句执行一句,重新执行需要再次翻译
计算机体系结构
计算机体系结构是对计算机组成的一种抽象性描述,表明计算机应包含哪些部分,如指令集、数据类型、存储器寻址技术、I/O 机理等。不同厂家的具体实现不尽相同,但是对高级语言的开发者来说,这些都是透明的,即底层实现不同不会影响到上层应用。
计算机的基本组成
冯·诺依曼计算机的特点
- 计算机由运算器、存储器、控制器、输入设备和输出设备组成
- 指令由操作码(表示操作的性质)和地址码(表示操作数在存储器中的位置)组成
- 指令和数据皆用二进制表示
- 指令和数据以同等地位存放于存储器内,并可按地址寻访
- 指令在存储器内按顺序存放,通常是顺序执行的。但也可根据运算结果或设定的条件,改变执行顺序
- 机器以运算器为中心,输入输出设备与存储器之间的数据传送通过运算器完成
计算机硬件框图
现代计算机可认为由三大部分组成:CPU、I/O 设备及主存储器(Main Memory,MM),如下图所示:
- M⋅M(Main Memory):主存储器,也就是常说的内存,与CPU直接交换信息。此外还有辅存,如硬盘、U盘等;
- ALU(Arithmetic Logic Unit):算数逻辑运算单元;
- CU(Contro Unit):控制单元,解释存储器中的指令,并发出各种命令执行指令。
细化的计算机组成框图
为了形象地了解计算机的工作过程,对现代计算机的组成框图进行细化:
运算器
运算器最少包含 3 个寄存器(现代计算机处理器内部往往设有通用寄存器组,如 ARM Cortex-A8 处理器,有 40 个 32bit 的寄存器(32 个通用寄存器,7 个状态寄存器,1 个程序计数器(PC,Program Counter)))和一个算数逻辑运算单元(ALU)。
- ACC(Accumulator):累加器
- MQ(Multiple-Quotient register):乘商寄存器
- X:操作数寄存器
控制器
控制器是计算机的神经中枢,由它指挥各部件自动、协调地运行。具体而言:
- 读取指令:命令存储器读出一条指令
- 分析指令:指出该指令需要完成什么操作,并按寻址特征指出操作数的地址
- 执行指令:根据操作数所在地址以及指令的操作码,完成某种操作
控制器由程序计数器(Program Counter,PC)、指令寄存器(Instruction Register,IR)和控制单元(CU)组成:
- PC:存放当前欲执行指令的地址,与主存的 MAR(Memory Address Register,存储器地址寄存器)有一条直接通路,且具备自动 +1 的功能,即可自动形成下一条指令的地址
- IR:存放当前的指令,其内容来自主存的 MDR。IR 中的操作码 (OP(IR)) 送至CU,用来分析指令。其地址码 (Ad(IR)) 作为操作数的地址,送至存储器的 MAR
- CU:分析当前指令所需完成的操作,并发出各种微操作序列,用以控制所有被控对象
主存储器
主存储器(简称主存或内存)包括存储体M、各种逻辑部件和控制电路等。存储体由许多存储单元组成,每个存储单元又包含很多存储元件,每个存储元件可以存储一位二进制代码 0 或 1。可见一个存储单元可存储一串二进制代码,称这串二进制代码为一个存储字,这串二进制代码的位数称为存储字长。存储字长可以是 8 位、16 位、32 位等。一个存储字可代表一个二进制数、一串十六进制字符、两个 ASCII 码或者一条指令。
每个存储单元都有自己的地址,主存的工作方式就是按照存储单元的地址实现对存储字各位的读写,而 MAR、MDR 则用来实现按地址访问:
- MAR(Memory Address Register):存储器地址寄存器,存放欲访问的存储单元的地址。其位数对应存储单元的个数,如 MAR 是 32 位,则共有 2^32 = 4 * 1024 * 1024 * 1024 (1024个记作 1K) 个存储单元
- MDR(Memory Data Register):存储器数据寄存器,存放从存储体读出的数据或准备写入存储体的数据(可以是代码,也可以是指令),其位数与存储字长相等
当然,要想实现一个完整的读/写操作,CPU还需要给主存发送各种控制信号,如读命令、写命令、地址译码驱动信号等。随着硬件电路的发展,主存都制成大规模集成电路的芯片,而将MAR、MDR 集成在 CPU 芯片中。
早期计算机的存储字长一般与机器的指令字长、数据字长相等,故访问一次主存便可取一条指令或一个数据。随着计算机应用范围的不断扩大,往往要求计算机的指令字长、数据字长是可变的。为了适应指令字长和数据字长的可变性,其长度不再由存储字长确定,而由字节的个数来表示。比如 4 字节的指令字长就是 32bit,2 字节的指令字长就是 16bit。至此,指令字长、数据字长和存储字长不必相等,但都必须是字节的整数倍。
下图为32位架构的ARM存储器组织结构,其基本数据类型有:
- Byte:字节,8位;
- HalfWord:半字,16位(半字必须与2字节边界对齐);
- Word:字,32位(字必须与4字节边界对齐);
- Double World(Cortex-A支持):双字,64位(双字必须与8字节边界对齐)。
计算机硬件的主要技术指标
机器字长
机器字长是指计算机进行一次整数运算所能处理的二进制数据的位数(整数运算即定点整数运算)。因为计算机中数的表示有定点数和浮点数之分,定点数又有定点整数和定点小数之分,这里所说的整数运算即定点整数运算。机器字长也就是运算器进行定点数运算的字长,即通用寄存器的位数。
主存字长一般等于机器字长,不等的情况下,一般是主存储器字长小于机器字长。例如机器字长是 32 位,主存储器字长可以是 32 位,也可以是 16 位。
Windows 64 位操作系统是针对 64 位机器字长的 CPU 设计的,目前 64 位架构实现技术主要有 AMD64、Intel EM64T 等。
存储容量
主存容量 = 存储单元数 * 存储字长
比如,若 MAR 是 32 位,则存储单元个数为: 2^32 = 4 * 1024 * 1024 * 1024个。若存储字长为 8 位,则存储容量 = 4 * 1024 * 1024 * 1024 * 8 bit,即 4G(4 Gigabyte) 。
运算速度
单位时间内执行的指令平均条数,单位 MIPS(Million Instruction Per Second)。
系统总线
同一时刻只能有一个部件向总线发送信息,但是可以有多个部件接受信息,因为总线是各部件共享的。
片内总线
芯片内部的连线,比如寄存器之间、寄存器与 ALU 之间的连线等。
系统总线
-
数据总线:双向传输,其条数称为数据总线宽度。数据总线宽度与机器字长、存储字长有关。比如总线宽度是 8 位,指令字长为 16 位,那么 CPU 取出一条指令,就需要访问两次主存。
-
地址总线:单向传输,指出数据总线上的源数据或目的数据所在存储单元的地址。地址总线的宽度与存储单元个数有关,比如 32 位的地址总线,可编址按字节寻址的存储单元个数为 2^32 = 4 * 1024 * 1024 * 1024 ,即 4 Gigabyte。
从存储器读一个字的数据时,首先由 CPU 将其地址经 MAR 通过地址总线送至主存,然后向主存发读命令。主存接到读命令后,将对应数据读出后,经数据总线送至 MDR。向存储器写一个字的数据时,CPU 先将目的地址经 MDR 通过地址总线送至主存,并将数据送至 MDR,然后向主存发写命令。主存接到写命令后,便可以将 MDR 中的数据经数据总线写至目的地址:
-
控制总线:决策总线使用权,用来发出各种控制信号。I/O 设备通过控制总线向 CPU 发出总线请求,CPU 通过控制总线向 I/O 设备发出读写命令。
-
通信总线:用于计算机系统之间或计算机系统与其它系统(如控制仪表、移动通讯等)之间通信。
虚拟存储系统
在虚拟存储系统中,程序员的编程地址范围与虚拟存储器的地址空间相对应。例如机器指令地址码是 32 位,那么虚拟存储器的存储单元个数可达 2^32 = 4 * 1024 * 1024 * 1024 个,若存储字长为 8 位,则存储容量为 4 Gegabyte,这可能比主存实际的存储单元个数多得多。这类指令地址码称为虚拟地址或逻辑地址,主存的实际地址称为物理地址。虚拟地址到物理地址的转换由操作系统负责实现,比如 Windows 操作系统通过页目和页表来实现虚拟地址到物理地址的转换。若虚拟地址指向的内容在主存,则可被 CPU 直接使用,否则必须先传到主存,然后才能被 CPU 访问。
结语
阅读《Android源代码情景分析》Binder 进程间通信系统一章,老罗(罗升阳,原书作者)列举 Google 开发Binder 作为 IPC 框架原因时说:“与传统的进程间通信机制相比,Binder 进程间通信机制在进程间传输数据时,只需要执行一次拷贝操作,因此,它不仅提高了效率,而且节省了内存空间”,对“只拷贝一次”有点疑惑:“Pipe 不也是只有一次吗?”(犯二了,其实是两次)
以 Pipe(无名管道,用于具有亲缘关系的进程间通信,比如父子进程、兄弟进程)为例,进程 A 向进程 B 发送数据,需要先将进程 A 用户空间中的数据拷贝至管道(在内核空间中),然后进程 B 再从管道中将数据拷贝至自己的用户空间,数据确实拷贝了两次。而 Binder 机制下,由于虚拟进程地址空间(vm_area_struct)和虚拟内核地址空间(vm_struct)都映射到同一块物理内存空间,当 Client 端与 Server 端发送数据时,Client(作为数据发送端)先从自己的进程空间把 IPC 通信数据 copy_from_user 拷贝到内核空间,而 Server 端(作为数据接收端)与内核共享数据,不再需要拷贝数据,而是通过内存地址空间的偏移量,即可获悉内存地址,整个过程只发生一次内存拷贝。效率最高的当属共享内存了,无需任何拷贝即可访问,只是需要结合信号量来进行信息同步。
一不小心就钻了牛角尖,为了搞清为什么 32位 CPU 最大寻址空间是 4G 和 Linux 每个进程独占 3G 用户空间的问题,把计算机组成原理又翻出来挑着看了一遍。
写文章还是挺累的,哪怕只是一篇总结(颈椎完全扛不住)。不过有第一篇,就会有第二篇。以前看过很多文章,都没有总结记录,时间长了,全忘了。。。
参考文献
[1] 计算机组成原理 高等教育出版社 2000-7 唐朔飞编著
[2] ARM嵌入式体系结构与接口技术 人民邮电出版社 2013-9 杨胜利 刘洪涛编著
[3] Android源代码情景分析 电子工业出版社 罗升阳编著
[4] Binder系列2—Binder Driver再探 Gityuan