本书主要面向应用型大学计算机类专业学生,从程序员的视角出发,围绕可执行文件的生成、加载和执行,重点介绍如何利用计算机系统相关知识来编写更有效的程序。全书将每个环节涉及的硬件和软件的基本概念关联起来,帮助学生建立完整的层次框架,从而加强系统观。本书共分8章,涵盖计算机系统基础、高级语言程序、数据的机器级表示、数据的基本运算、指令集体系结构、程序的机器级表示、程序的链接、程序的加载和执行等内容。本书内容详尽、概念清楚、实例丰富,适合作为高等学校计算机专业计算机系统相关课程的教材,也适合相关专业的研究生和技术人员阅读参考。
本书主要面向应用型大学计算机类专业学生,从程序员的视角出发,围绕可执行文件的生成、加载和执行,重点介绍如何利用计算机系统相关知识来编写更有效的程序。
前 言
随着计算机信息技术的飞速发展,早期多人一机的主机终端模式发展为PC(个人计算机)时代的一人一机模式,又发展为如今的人机物互联的智能化大数据并行计算模式。现如今各行各业都离不开计算机信息技术,计算机信息产业对我国现代化战略目标的实现发挥着极其重要的支撑作用。这对计算机专业人才培养提出了更高的要求,原先传统的计算机专业教学课程体系和教学内容已经远远不能反映现代社会对计算机专业人才的培养要求,特别是传统课程体系按计算机系统抽象层划分课程,不同课程内容之间相互割裂,软件和硬件分离,导致学生无法形成计算机系统的整体概念,缺乏系统思维。为此,过去十多年来,高等学校计算机类专业教学指导委员会在全国开展了计算机类专业系统能力培养教学改革,重新规划教学课程体系,调整教学理念和教学内容,加强学生系统能力培养,使学生能够深刻理解计算机系统整体概念,更好地掌握软/硬件协同设计和并行程序设计技术,从而更多地培养出满足业界需求的各类计算机专业人才。
虽然不同类型高校的计算机类专业人才培养目标有所区别,例如,对于应用型大学计算机专业学生来说,毕业后绝大部分将从事计算机系统应用开发工作而不会直接从事计算机硬件和系统软件的设计开发工作,但是,不管培养计算机系统哪个层面的技术人才,计算机专业教育都要重视学生系统观的培养。本书的主要目的就是为加强计算机专业学生的系统观而提供一本关于计算机系统导论课程教学的教材,该课程主要面向应用型大学的计算机类专业课程体系而设置。
本书的写作思路和内容组织
本书从程序员视角出发,重点介绍应用程序员应该如何利用计算机系统相关知识来编写更有效的程序。本书以高级语言程序的开发和运行过程为主线,将该过程中每个环节涉及的硬件和软件的基本概念关联起来,试图使读者建立完整的计算机系统层次结构框架,了解计算机系统全貌和相关知识体系,初步理解计算机系统中每个抽象层及其相互转换关系,建立高级语言程序、ISA(指令集体系结构)、OS(操作系统)、编译器、链接器之间的相互关联,对指令在硬件上的执行过程和指令的底层硬件执行机制有一定的认识和理解,从而增强程序调试、性能优化、移植和健壮性保证等方面的能力,并为后续相关课程的学习打下坚实基础。
本书从高级语言程序中的变量/常量及其运算、控制结构语句和函数调用出发,将C语言程序与后续各章节的内容建立关联并进行导引。本书的具体内容包括数据在机器中的表示、数据在机器中的基本运算、指令集体系结构的基本内容、程序中各类控制语句和函数调用对应的机器级代码结构、复杂数据类型的分配和访问、缓冲区溢出及其攻击和防范、可执行目标代码的链接和加载、可执行文件中指令序列的基本执行过程等。
不管构建一个计算机系统的各类硬件和软件差别有多大,计算机系统的构建原理以及在计算机系统上程序的转换和执行机理都是相通的,因而,本书仅介绍一种特定计算机系统平台下的相关内容。
本书共分8章:第1章是计算机系统概述;第2章是高级语言程序;第3章和第4章分别介绍高级语言程序中数据的机器级表示及其在机器中的各类基本运算方法;第5章主要介绍IA-32/x86-64指令系统;第6章介绍C语言程序中的函数调用和各类语句所对应的底层机器级表示,展示高级语言程序与机器级语言程序的对应转换关系;第7章主要介绍如何将不同的程序模块链接起来构成可执行目标文件,展示程序的链接环节;第8章简要介绍程序的加载和执行,包括进程的存储器映射和进程的上下文切换,以及用于执行程序的CPU的基本功能和基本组成。
读者所需的背景知识
本书假定读者有一定的C语言程序设计基础,已经掌握了C语言的语法和各类控制语句、数据类型及其运算、各类表达式、函数调用和C语言的标准库函数等相关知识。
此外,本书介绍了计算机中的基本运算电路和程序中指令的执行过程,理解这些内容需要掌握布尔代数、逻辑表达式和基本逻辑门电路等基础知识,因而本书假定读者具有数字逻辑电路课程的基础。如果读者不具备这些背景知识也没有关系,本书4.1节对布尔代数和数字逻辑基础进行了简要介绍,在此基础上可进一步学习和理解基本运算电路与指令执行过程等内容。
本书所用平台为IA-32/x86-64 Linux GCC C语言。书中大多数C语言程序对应的机器级表示都是基于IA-32/x86-64 Linux平台用GCC编译器生成的。本书会在介绍程序的机器级表示之前,先简要介绍IA-32/x86-64指令集体系结构,包括其机器语言和汇编语言,因而,读者不需要具有任何机器语言和汇编语言的背景知识。
对于gcc工具软件和GDB调试软件的使用方法,本书附录中做了简要介绍,许多使用上的细节问题请读者自行在网上搜索答案。
本书在课程体系中的位置
传统计算机专业课程体系设置多按计算机系统层次结构进行横向切分,自下而上分解成数字逻辑电路、计算机组成原理、汇编程序设计、操作系统、编译原理、程序设计等课程,而且,每门课程都仅局限在本抽象层,相互之间几乎没有关联,因而学生对整个计算机系统的认识过程就像盲人摸象一样,很难形成对完整计算机系统的全面认识。
本书在内容上注重计算机系统各抽象层的纵向关联,将高级语言程序、汇编语言程序、机器代码及其执行串联起来,为学生进一步学习后续相关课程打下坚实的基础,适合在完成程序设计基础课程后进行学习。本书内容贯穿计算机系统的各个抽象层,是关于计算机系统的最基础的内容,因而使用本书作为教材开设的课程适用于所有计算机相关专业。使用本书作为教材开设的课程名称可以是计算机系统导论 计算机系统基础等,该课程作为高级语言程序设计课程的解疑答惑课程,在程序设计课程、讲解程序编译转换及程序运行和系统管理机制等系统类课程之间起着承上启下的作用,因此该课程可以是后续所有系统类课程的前导课程。
如何阅读本书
本书试图将计算机系统各抽象层涉及的重要概念通过程序的开发和加载、执行为主线串接起来,因而本书涉及的所有问题和内容都是从程序出发的,这些内容涉及程序中数据的表示及运算,或者涉及程序对应的机器级表示,或者涉及多个程序模块的链接,或者涉及程序的加载及执行,或者涉及程序执行过程中的异常中断事件等。从读者熟悉的程序开发和加载、执行出发来介绍计算机系统基本概念,可以使读者将新学的概念与已有的知识建立关联,不断拓展和深化知识体系。特别是,因为所有内容都从程序出发,所以所有内容都可以通过具体程序进行验证,读者可以在边学边做中将所学知识转化为实践能力。
本书虽然涉及内容较广,但所有内容之间具有非常紧密的关联,因而,建议读者在阅读本书时采用整体性学习方法:通过对第1章的学习先建立一个粗略的计算机系统整体框架;然后通过第2章高级语言程序的相关内容,一边回顾前导课程所学内容,一边将高级语言程序中的相关内容与后续各章内容建立关联;再不断地通过对后续章节的学习,将新的内容与前面所学内容贯穿起来,以逐步细化计算机系统框架内容,最终形成比较完整的、相互密切关联的计算机系统整体概念。
本书提供了大量的例题和课后习题,这些题目大多是具体的程序示例,通过对这些示例的分析或验证性实践,读者可以对基本概念有更加深刻的理解。因此,读者在阅读本书时,若遇到一些难以理解的概念,可以先不用仔细琢磨,而是通过具体的程序示例来对照基本概念和相关手册中的具体规定进行理解。
本书提供的小贴士对理解书中的基本概念很有用,由于篇幅有限,这些补充资料不可能占用很大篇幅,大多是简要内容。如果读者希望了解更多细节,可以自行到网上查找。
本书虽然涉及高级语言程序设计、数字逻辑电路、汇编语言程序、计算机组成与系统结构、操作系统、编译和链接等内容,但是,本书主要讲解它们之间的关联,而不提供其细节,如果读者想要了解更详细的关于数字系统设计、操作系统、编译技术、计算机体系结构等方面的内容,还是要阅读相关的专业书籍。不过,若读者学完本书后再去阅读专业书籍,则会轻松很多。
致谢
衷心感谢在本书的编写过程中给予我热情鼓励和中肯建议的各位专家、同事和学生,正是因为有他们的鞭策、鼓励和协助,本书的编写才能顺利完成。
在本书的编写过程中,得到了国防科技大学王志英教授、北京航空航天大学马殿富教授、西北工业大学周兴社教授、武汉大学何炎祥教授、北京大学陈向群教授等各位专家的悉心指导和热情鼓励,浙江大学城市学院杨起帆教授对本书提出了许多宝贵的修改意见,西安邮电大学陈莉君教授、山东大学杨兴强教授和中国石油大学(华东)张琼声副教授从书稿的篇章结构到内容各方面都提出了许多宝贵的意见,中国海洋大学蒋永国副教授对本书的编写修改提出了很好的建议,中国石油大学(华东)范志东同学提供了第7章中某可执行文件程序头表中的部分信息,在此表示衷心的感谢。
本书以我在南京大学讲授的计算机组成与系统结构和 计算机系统基础两门课程的部分讲稿内容为基础,感谢南京大学各位同人和各届学生对讲稿内容与教学过程所提出的宝贵反馈和改进意见,这使本书的内容得以不断改进和完善。本书第二作者余子濠博士对书中大部分程序进行了验证,并对书中相关内容与对应手册中的规定进行了详细的核对和审查,对一些关键内容提出了有益的修改意见。
结束语
本书广泛参考了国内外相关的经典教材和教案,在内容上力求做到取材先进并反映技术发展现状;在内容的组织和描述上力求概念准确、语言通俗易懂、实例深入浅出,并尽量利用图示和实例来解释和说明问题。但是,由于计算机系统相关技术仍在不断发展,新的思想、概念、技术和方法不断涌现,加之作者水平有限,在编写中难免存在不当或遗漏之处,恳请广大读者对本书的不足之处给予指正,以便在后续的版本中予以改进。
袁春风 于南京
2023年3月
目 录
前言
第1章 计算机系统概述 1
1.1 计算机基本工作原理 1
1.1.1 冯·诺依曼结构基本思想 1
1.1.2 冯·诺依曼模型机基本结构 2
1.1.3 程序和指令的执行过程 4
1.2 程序的开发与运行 7
1.2.1 程序设计语言和翻译程序 7
1.2.2 从源程序到可执行文件 9
1.2.3 可执行文件的启动和执行 11
1.3 计算机系统的层次结构 12
1.3.1 计算机系统抽象层的转换 12
1.3.2 计算机系统核心层之间的关联 14
1.3.3 计算机系统的不同用户 17
1.4 本书的主要内容和组织结构 19
1.5 小结 21
习题 22
第2章 高级语言程序 23
2.1 C语言概述 23
2.2 变量和常量及其类型 24
2.2.1 C程序中的变量及其类型 24
2.2.2 C程序中的常量及其类型 25
2.3 表达式及运算符 26
2.3.1 C语言表达式中的运算符 26
2.3.2 C语言程序中的运算 27
2.4 控制结构和函数调用 29
2.4.1 C语言中的控制结构 29
2.4.2 C语言中的函数调用 30
2.4.3 变量的作用域及其存储分配 32
2.4.4 C标准I/O库函数 35
2.5 小结 37
习题 37
第3章 数据的机器级表示 39
3.1 二进制编码和进位计数制 39
3.1.1 信息的二进制编码 39
3.1.2 进位计数制 40
3.1.3 进位计数制之间数据的转换 41
3.2 整数的表示 44
3.2.1 定点数的编码表示 44
3.2.2 无符号整数和带符号整数的表示 49
3.2.3 C语言中的整数及其相互转换 49
3.3 浮点数的表示 52
3.3.1 浮点数的表示范围 52
3.3.2 浮点数的规格化 53
3.3.3 IEEE 754浮点数标准 53
3.3.4 C语言中的浮点数类型 57
3.4 非数值数据的编码表示 59
3.4.1 位串或逻辑值 59
3.4.2 西文字符 59
3.4.3 汉字字符 60
3.5 数据的宽度和存储 62
3.5.1 数据的宽度和长度单位 62
3.5.2 数据的存储和排列顺序 64
3.5.3 数据扩展和数据截断操作 69
3.6 小结 70
习题 70
第4章 数据的基本运算 74
4.1 布尔代数和逻辑运算 74
4.1.1 布尔代数 74
4.1.2 逻辑电路基础 76
4.2 基本运算电路 77
4.2.1 多路选择器 77
4.2.2 全加器和加法器 77
4.2.3 带标志信息加法器 78
4.2.4 算术逻辑部件 79
4.3 整数加减运算 80
4.3.1 补码加减运算器 80
4.3.2 无符号整数加减运算 83
4.3.3 带符号整数加减运算 84
4.3.4 对整数加减运算结果的解释 85
4.4 整数的乘运算 86
4.4.1 无符号数乘法运算 87
4.4.2 原码乘法运算 88
4.4.3 补码乘法运算 89
4.4.4 两种整数乘的关系 90
4.5 整数的除运算 92
4.5.1 无符号数除法运算 93
4.5.2 原码除法运算 94
4.5.3 补码除法运算 96
4.6 整数常量的乘除运算 97
4.7 浮点数运算 98
4.7.1 浮点数加减运算 99
4.7.2 浮点数乘除运算 104
4.7.3 浮点运算异常和精度 105
4.8 小结 107
习题 108
第5章 指令集体系结构 113
5.1 程序转换概述 113
5.1.1 机器指令与汇编指令 113
5.1.2 指令集体系结构概述 115
5.1.3 生成机器代码的过程 116
5.2 IA-32指令系统概述 122
5.2.1 数据类型及格式 123
5.2.2 寄存器组织 124
5.2.3 操作数的寻址方式 128
5.2.4 机器指令格式 132
5.3 IA-32常用指令类型 133
5.3.1 传送指令 133
5.3.2 定点算术运算指令 137
5.3.3 按位运算指令 140
5.3.4 程序执行流控制指令 142
5.3.5 x87浮点处理指令 147
5.3.6 MMX/SSE指令集 150
5.4 兼容IA-32的64位系统 152
5.4.1 x86-64的发展简史 152
5.4.2 x86-64的基本特点 153
5.4.3 x86-64的基本指令 154
5.5 小结 157
习题 157
第6章 程序的机器级表示 160
6.1 过程调用的机器级表示 160
6.1.1 IA-32中过程的调用约定 160
6.1.2 变量的作用域和生存期 164
6.1.3 按值传递参数和按地址传递
参数 166
6.1.4 递归过程调用 171
6.1.5 非静态局部变量的存储分配 173
6.1.6 x86-64的过程调用 176
6.2 流程控制语句的机器级表示 181
6.2.1 选择语句的机器级表示 182
6.2.2 循环结构的机器级表示 186
6.3 复杂数据类型的分配和访问 189
6.3.1 数组的分配和访问 189
6.3.2 结构体数据的分配和访问 193
6.3.3 联合体数据的分配和访问 196
6.3.4 数据的对齐 198
6.4 越界访问和缓冲区溢出 201
6.4.1 缓冲区溢出 201
6.4.2 缓冲区溢出攻击 203
6.4.3 缓冲区溢出攻击的防范 206
6.5 小结 209
习题 210
第7章 程序的链接 223
7.1 编译、汇编和静态链接 223
7.1.1 预处理、编译和汇编 223
7.1.2 可执行目标文件的生成 225
7.2 目标文件格式 227
7.2.1 ELF目标文件格式 227
7.2.2 可重定位目标文件格式 228
7.2.3 可执行目标文件格式 232
7.2.4 可执行文件的存储器映射 234
7.3 符号表和符号解析 236
7.3.1 符号和符号表 236
7.3.2 符号解析 239
7.3.3 与静态库的链接 243
7.4 重定位 246
7.4.1 重定位信息 246
7.4.2 重定位过程 247
7.5 动态链接 251
7.5.1 动态链接的特性 251
7.5.2 程序加载时的动态链接 252
7.5.3 程序运行时的动态链接 253
7.5.4 位置无关代码 255
7.6 小结 260
习题 260
第8章 程序的加载和执行 266
8.1 进程与可执行文件的加载 266
8.1.1 程序和进程的概念 266
8.1.2 Linux系统的虚拟地址空间 267
8.1.3 进程的存储器映射 270
8.1.4 程序的加载过程 271
8.2 进程的控制 274
8.2.1 进程的逻辑控制流 275
8.2.2 进程的上下文切换 276
8.3 程序执行与CPU基本组成 278
8.3.1 程序及指令的执行过程 278
8.3.2 打断程序正常执行的事件 280
8.3.3 CPU的基本功能和组成 281
8.4 小结 283
习题 284
附录A gcc的常用命令行选项 287
附录B GDB的常用命令 288
参考文献 291