
另外一本大名鼎鼎的书 CSAPP
第一章 计算机系统漫游
1.1 信息就是位 + 上下文
系统中的所有信息,包括磁盘文件,内存中的程序和用户数据以及通过网络传输的数据,都是用一系列比特表示的。
如何读取和解释这些比特只取决于比特所在的上下文。比如说,在不同的上下文环境中,同的字节序列可能代表整数、浮点数、字符串或者机器指令。
这个思想会在第二章详细介绍
1.2 程序被其他程序翻译成不同的形式
我们平时写的 .c
文件是一种高级语言,人类很容易阅读,但是机器很难理解它们。所以C语句必须被翻译成更低级的机器指令,并将这些机器指令打包进可执行目标文件(executable object program)里后才能在机器上运行。
在 UNIX 系统上,从源文件到目标文件经历以下步骤:

源文件 hello.c
先后经过 预处理器、编译器、汇编器 和 链接器 的处理,最终称为可执行文件
预处理阶段
预处理器(cpp)对所有 以
#
开头的预处理语句进行处理。例如,读取stdio.h
文件中的全部内容,并将其直接插入#include<stdio.h>
所在的位置。处理后的文件成为了另一个C 程序,通常以.i
结尾 ,hello.i
编译阶段
编译器 (cc1) 将
hello.i
程序翻译成汇编语言文件hello.s
汇编阶段
汇编器 (as) 将
hello.s
翻译为机器语言,产生的文件称为 可重定位的目标文件(relocatable object program) ,hello.o
链接阶段
链接器 (ld) 处理各个
.o
文件之间的引用关系。例如,hello.o
文件中引用了标准库中的printf
函数,而printf
函数位于一个预先编译好的printf.o
文件 中,链接器负责处理这个融合(merge)的过程。处理后的hello
文件是一可执行文件。
1.3 理解编译系统如何工作是大有益处的
好处有:
- 优化程序性能
- 理解链接时错误
- 避免安全漏洞
1.4 处理器从内存中读取并翻译指令
1.4.1 系统的硬件组成

总线 (Buses)
总线是连接计算机系统内各个部件的渠道。它通常一次只能 传输固定字节数,也称为 字(words)。
这是系统的一个重要参数,比如我们通常说的 32位/64位,说的就是总线一次能够传输 4字节还是8字节
I/O 设备
包括键盘、鼠标、显示器、磁盘(disk)
主存 (main memory)
处理器
central processing unit (CPU) 是整个计算机系统的引擎。它的内核中有一个大小为一个字的存储设备,称为 程序计数器(program counter,PC)。这个 PC 指向存储在主存中的机器指令,那条指令就是CPU接下来要执行的。
通常,CPU都执行一些非常简单的由指令集架构定义的指令。这种指令通常不是很多,它们围绕 主存(main memory)、寄存器文件(register file)和算数逻辑单元(arithmetic/logic unit ,ALU) 这三者进行。寄存器文件由一系列大小为字长的寄存器组成,每个寄存器都有唯一的名字。
下面通过一个例子解释它们是如何运作的:
- 载入:从主存复制一个字节或者一个字到一个寄存器中
- 存储:从寄存器复制一个字节或者一个字到主存中
- 操作:将两个寄存器中的内容拷贝到 ALU 中,由 ALU 对这两个寄存器中的内容进行算数操作,并将结果存储在一个寄存器中
- 跳跃:提取一条指令并赋值给 PC
1.4.2 运行 hello 程序

当我们在键盘上 键入 ./hello
时,shell 将每一个字符都读取并保存在 register 中,随后 所有字符都通过CPU被保存在主存中,如 上图所示

当输入完成并 键入 enter
键后,shell 将磁盘上的 hello 文件读取到 主存中,等待执行,如上图所示

一旦程序和数据被读取到主存中,CPU就开始执行程序中的机器指令,并将 hello,world\n
打印在输出设备上,如上图所示
1.5 缓存那些事
从上一节中我们可以看到:程序执行时需要多次拷贝代码和数据,这对于性能显然是不利的。
为了让这些耗时的拷贝转移工作更高效,人们设计出了 cache memories 缓存。它用来暂时存储以后可能会用到的数据。
存储系统的结构图示例

SRAM:static random access memory
DRAM:dynamic random access memory
1.6 不同的存储设备形成了一个层级结构
第六章会详细讨论
1.7 操作系统 管理着 硬件
回顾一下,当我们执行 hello
程序时,该程序本身并没有亲自 读/写 键盘,显示器,磁盘或主存,而是依赖于系统提供的服务。
我们可以把操作系统想象成硬件和程序之间的一层软件,所有对硬件的操作必须经过操作系统。如下图所示

操作系统的实现基于一些抽象,如下图所示:

文件是 I/O 设备的抽象
虚拟内存是 主存 和 I/O 设备的抽象
进程是 处理器、主存 和 I/O 设备的抽象
1.7.1 进程
进程是对运行中程序的一种抽象。每个进程都似乎独占所有硬件,而CPU可以在不同进程之间切换以同时运行多个进程。实现这种切换的机制被称为 上下文切换
1.7.2 线程
线程是进程拥有的不同的执行单元(execution units),它们共享进程的代码和数据
1.7.3 虚拟内存
虚拟内存的作用:让不同的进程无差别的使用存储空间。
它建立 真实地址空间 到 虚拟地址空间( virtual address space) 的映射
进程的虚拟地址空间示意图:

- Kernel virtual memory. 位于虚拟地址空间的顶部,应用程序不能读写此部分内存,它们必须由内核 读写/操作
1.7.4 文件
文件就是一系列字节,仅此而已。
所有的 I/O 设备都被抽象为文件,对它们的读写都使用被称为 Unix I/O 的系统调用完成
1.8 不同系统通过网络进行通信
网络也可以被抽象为一个 I/O 设备(其实它就是一个 I/O 设备),它和计算机系统的其他组成部件的关系如下图

1.9 一些重要的主题
1.9.1 阿姆达尔定律
阿姆达尔定律阐述了 如何衡量 系统某个部分性能提升 对 系统整体性能提升 的贡献
取决于两个方面:
- 此部分在整个系统中的重要性
- 此部分性能提升的程度
现在假设系统运行某一程序需要的时间为 $T_{old}$ ,系统某一部分花费的时间占比为 $\alpha$ ,此部分性能提升的系数为 $k$ 。也就是说,此部分原来需要花费的时间为 $\alpha T_{old}$ ,现在需要的时间为 $(\alpha T_{old})/k$ 。那么系统现在运行这个程序需要的时间为
整个系统性能的提升 $S$ 为
练习题 1.1
假设你是一个卡车司机,现在你要将一批土豆从 Boise, Idaho 运输到 Minneapolis, Minnesota,整个行程共 2500 公里。你估计运输途中的平均时速为 100km/h ,整个行程要花费 25 小时。
A. 你从新闻上得知 Montana 将它辖区内道路的限速提高到了 150km/h ,这段路程整整有 1500 公里。那么你的整个行程将提速多少?
$\alpha = 0.6$ , $k = 1.5$
B.你可以为你的卡车更换新的涡轮增压器以提高行驶速度。请问在 Montana 辖区内的速度必须达到多少,才能将整个行程的速度提高 1.67 倍?
$S = 1.67$,$\alpha = 0.6$
速度至少提高到 300km/h
练习题 1.2
一家汽车生产商将推出有 4 倍性能提升的新引擎。而你被安排参与新引擎的研发工作。已知引擎只有 90% 的部件可以升级。那么,应该将此部分性能提升多少倍才能达到宣传标准呢?
至少将可升级部分的性能提高 6 倍才能达到宣传的标准
1.9.2 并发和并行
并发用来描述同时运行多个程序的系统;而并行指使用并发来提高系统性能
并行可以在很多层次上进行研究
线程级别的并发
多核处理器结构示意图:
此多核处理器有四个 核 ,每个核有 L1 和 L2 缓存,其中 L1 缓存又分为 数据缓存(d-cache)和 指令缓存(i-cache),L3 缓存由所有核共享
超线程技术Hyperthreading
指的是允许单个CPU同时执行多个控制流的技术
It involves having multiple copies of some of the CPU hardware, such as program counters and register files, while having only single copies of other parts of the hardware, such as the units that perform floating-point arithmetic.
指令级别的并行
单一指令,多数据(SIMD)并行