- 计算机组成原理(基于x86-64架构)
- (美)罗伯特·G.普兰茨
- 3259字
- 2025-05-19 16:07:01
前言
本书从程序员的角度介绍了计算机硬件的工作原理及其相关概念。硬件由一组机器指令控制。这些指令控制硬件的方式称为指令集架构(Instruction Set Architecture,ISA)。程序员的工作是设计这些指令的序列,使硬件执行操作来解决问题。
几乎所有的计算机程序都是用高级语言编写的,其中有些语言是通用的,有些则面向特定类型的应用。但它们都旨在为程序员提供一套编程结构,使其更适合用人类的语言解决问题,而不是直接涉及指令集架构和硬件细节。
目标读者
当你使用高级语言编写程序的时候,你有没有想过幕后发生了什么?你知道计算机可以通过编程来做出判断,但这是怎么实现的呢?你可能知道数据是以比特存储的,但是存储一个十进制数意味着什么呢?本书的目标是回答这些和其他很多关于计算机如何工作的问题。我们将着眼于硬件组件和用于控制硬件的机器指令。
我假设你了解高级语言编程的基本知识,但你不必是专业的程序员。在讨论了硬件组件之后,我们讲解并编写大量汇编语言程序,汇编语言是一种能够直接翻译成机器指令的语言。
与大多数汇编语言图书不同,本书没有把重点放在使用汇编语言编写应用程序上。诸如C++、Java、Python这样的高级语言在创建应用程序时效率更高。使用汇编语言编程是一个乏味、容易出错且耗时的过程,应该尽可能避免。我们的目标是学习编程概念,而不是创建应用程序。
关于本书
在创作本书的过程中,我秉持以下观点:
● 在已知概念的基础上学习会更容易;
● 现实世界中的硬件和软件为学习理论概念提供了一个更有趣的平台;
● 学习工具应该便宜且容易获得。
书中的编程
本书基于x86-64指令集架构,这是x86指令集架构的64位版本,也称为AMD64、x86_64、x64或Intel 64。本书所有的编程都是在Linux操作系统Ubuntu下的GNU编程环境中完成的。这些程序稍作修改(如果需要),应该就可以在大多数常用的Linux发行版中运行。
本书使用C作为高级语言,后续会用到一些C++。不过就算不懂C/C++也不用担心。本书展示的所有C/C++编程都非常简单,在编程过程中我会给出必要的讲解。
学习汇编语言时有一个重要问题是要在程序中使用键盘和终端屏幕。对键盘输入和屏幕输出进行编程不是件容易事,远远超出了初学者的专业知识范围。GNU编程环境提供了C标准库。为了符合本书的“现实世界”标准,我们会用到标准库中的函数,这些库函数从汇编语言中调用起来非常方便,能够帮助我们在应用程序中使用键盘和屏幕。
x86-64指令集架构包括大约1500条指令。确切的数量取决于对“怎样算是不同的指令”的界定,但是要记住的指令实在太多了。一些汇编语言图书通过发明一种“理想化的”指令集架构来阐述概念,从而解决了这个问题。同样,为了保持本书的“现实世界”性质,我将使用标准的x86-64指令集,但只从中选择了足以说明基本概念的一个指令子集。
为什么要阅读本书
鉴于有许多优秀的高级语言可供你编写程序,同时也不必关心机器指令如何控制硬件,你可能会好奇为什么还要阅读本书。所有高级语言最终都会被翻译成控制硬件的机器指令。熟悉硬件的功能以及指令如何控制硬件有助于你理解计算机的功能和局限性。我相信这种理解可以让你成为更优秀的程序员,哪怕你使用的是高级语言。
如果你的主要兴趣是硬件,理解程序如何使用硬件也很重要。
你可能喜欢汇编语言编程,并希望继续学习下去。例如,如果你对系统编程感兴趣——编写部分操作系统、编写编译器,甚至设计另一种更高级的语言——通常也离不开汇编语言层面的知识。
在嵌入式系统编程领域也存在很多具有挑战性的机会,在这些系统中,计算机承担着专门的任务。手机,家用电器,汽车,供暖、通风与空调系统,医疗设备等,都是我们日常生活中不可或缺的一部分。嵌入式系统是物联网技术的重要组成部分。对嵌入式系统进行编程通常需要理解计算机如何在汇编语言层面与各种硬件设备进行交互。
如果你已经学过其他处理器的汇编语言,本书可以作为参考手册。
章节组织
本书大致分为3部分——数学和逻辑、硬件、软件。数学部分旨在为你提供讨论概念所需的数学语言。硬件部分介绍了用于构建计算机的组件。
这两部分为讨论软件如何控制硬件提供了背景知识。我们将学习C语言中的每一种基本编程结构,本书的最后还会涉及一些C++,并介绍编译器如何将C/C++代码翻译成汇编语言,后者是一种直接访问指令集架构的语言。我还会向你展示程序员如何直接使用汇编语言编写等效的结构。
第1章讲述了计算机的3个子系统及其连接方式,还讨论了如何设置书中用到的编程工具。
第2章展示了如何使用二进制和十六进制数字系统存储无符号整数以及字符的ASCII编码。本章我们将编写本书的第一个C程序,并使用gdb调试器来探究这些概念。
第3章讲述了无符号和有符号整数的加法和减法,并解释使用固定位数表示整数的限制。
第4章讲述了布尔代数运算符和函数,以及使用代数工具和卡诺图的函数最小化。
第5章从电子学简介开始,然后讨论逻辑门以及如何使用CMOS晶体管构建逻辑门。
第6章讨论了没有记忆功能的逻辑电路,包括加法器、译码器、复用器和可编程逻辑设备。
第7章讨论了具有记忆功能的时钟和非时钟逻辑电路,以及使用状态转换表和状态图的电路设计。
第8章讲述了存储器层级结构——云、大容量存储、内存、缓存、CPU寄存器。此外还讨论了寄存器、SRAM、DRAM的存储器硬件设计。
第 9 章概述了CPU子系统。本章还解释了指令执行周期和主要的x86-64寄存器,并展示了如何在gdb调试器中查看寄存器内容。
第 10 章讨论了由编译器生成的汇编语言版本和直接使用汇编语言编写的最小C函数。本章介绍了汇编器指令和CPU指令。我列举了一个使用gdb文本用户界面作为学习工具的例子。在章末还提供了AT&T语法的简要说明。
第 11 章讲述了在寄存器中传递参数、位置无关代码以及使用调用栈传递返回地址和创建自动变量。
第12章着眼于指令如何在比特级编码,还介绍了寻址模式和跳转指令,以及汇编器和链接器。
第13章介绍while、do-while、for、if-else和switch控制流结构的汇编语言实现。
第14章讲述了函数如何访问外部变量——全局变量、按值传递、按指针传递和按引用传递。本章总结了栈帧的结构。
第15章展示了递归的工作原理,还讨论了使用汇编语言通过函数或内联汇编访问高级语法无法直接触及的CPU硬件特性。
第16章讲述了位掩码、移位以及乘法和除法指令。
第17章解释了数组和记录(结构体)的实现方式以及在汇编语言层面的访问方法。
第18章展示了结构体如何用作C++对象。
第19章讲述了定点数和浮点数、IEEE 754标准以及部分SSE2浮点指令。
第20章将I/O与内存和总线时序进行比较。讲述了隔离I/O和内存映射I/O。本章概述了轮询式I/O编程,讨论了中断驱动I/O和直接内存访问。
第21章简要讲述了x86-64如何处理中断和异常。本章包括使用int 0x80和syscall在没有C运行时环境的情况下执行系统调用的例子。
高效使用本书
鉴于本书的组织形式,如果你遵循一些简单的指导方针,定能事半功倍。
许多章节的结尾都提供了练习,让你有机会实践章节的主要内容。这些只是练习,不是测试。事实上,我已经在我的GitHub主页(请搜索rgplantz)上给出了大部分练习的答案和我的解决方案。
如果你是将本书作为教材的教师,实在抱歉,你只能自己出考试题了。许多练习都有相当明显的拓展空间,教师可以据此设计课堂作业。
为了有效地利用这些练习,我推荐一种迭代方法。
(1)试着自己解决问题。花些时间思考,但是不要太久。
(2)如果不知道答案,看看我的解决方案。在某些情况下,我会在提供完整的解决方案之前给出提示。
(3)回到步骤(1),请教有经验的汇编语言程序员,看他们如何解决这个问题。
我强烈建议你做的一件事是自己输入代码。我相信这能帮助你更快地学习。如果不出意外,你不得不阅读代码中的每个字符。我认为从在线解决方案中复制和粘贴代码没有任何好处。说实话,本书中的程序没有什么实用性。这些代码是为了你自己动手练习而准备的,所以请本着这种精神使用代码。
这种实操方法也适用于前几章中的数学内容,包括在多种基数之间转换数字。虽然大多数计算器都能轻松实现数制转换,但转换并不是重点,重点是了解如何用位模式表示数值。我相信纸笔运算有助于你理解这些位模式。
在第1章中,我们将首先对计算机的主要子系统展开宏观的概述。然后,介绍如何在计算机上设置编程环境,以创建和运行本书中的程序。