深入理解计算机系统书籍 (1)

第一章 计算机系统漫游

1.处理器读并解释储存在内存中的指令

1.硬件组成


3. 主存

主存是一个临时存储设备,在处理器执行程序时,用来存放程序和程序处理的数据。

从 物理上来说,主存是由一组动态随机存取存储器(DRAM) 芯片组成的。

从逻辑上来说,存储 器是一个线性的字节数组,每个字节都有其唯一的地址(数组索引),这些地址是从零开始 的。

一般来说,组成程序的每条机器指令都由不同数量的字节构成。

与C 程序变量相对应的 数据项的大小是根据类型变化的。

比如,在运行Linux 的 x86-64机器上,short 类型的数据 需要2个字节, int 和 float 类型需要4个字节,而 long 和 double 类型需要8个字节。

4. 处理器

中央处理单元(CPU), 简称处理器,是解释(或执行)存储在主存中指令的引擎。

处理 器的核心是一个大小为一个字的存储设备(或寄存器),称为程序计数器(P C) 。

在任何时 刻 ,PC 都指向主存中的某条机器语言指令(即含有该条指令的地址)。

从系统通电开始,直到系统断电,处理器一直在不断地执行程序计数器指向的指令, 再更新程序计数器,使其指向下一条指令。

处理器看上去是按照一个非常简单的指令执行 模 型 来 操 作 的 , 这 个 模 型 是 由指 令 集 架 构决 定 的 。

在 这 个 模 型 中 , 指 令 按 照 严 格 的 顺 序 执 行,而执行一条指令包含执行一系列的步骤。

处理器从程序计数器指向的内存处读取指令,解释指令中的位,执行该指令指示的简单操作,然后更新 PC, 使其指向下 一 条指令, 而这条指令并不 一 定和在内存中刚刚执行的指令相邻。

这样的简单操作并不多,它们围绕着主存、 寄 存 器 文 件(register file)和 算 术/逻 辑 单 元(ALU) 进行。

寄存器文件是一个小的存储设备,由一些单个字长的寄存器组成,每个 寄存器都有唯一的名字。ALU 计算新的数据和地址值。

下面是一些简单操作的例子, CPU 在指令的要求下可能会执行这些操作。

● 加 载:从主存复制 一 个字节或者 一 个字到寄存器,以覆盖寄存器原来的内容。

● 存储:从寄存器复制 一 个字节或者 一 个字到主存的某个位置,以覆盖这个位置上原 来的内容。

●操作:把两个寄存器的内容复制到 ALU,ALU 对这两个字做算术运算,并将结果 存放到 一 个寄存器中,以覆盖该寄存器中原来的内容。

●跳转:从指令本身中抽取一个字,并将这个字复制到程序计数器(PC) 中,以覆盖 PC 中原来的值。

处理器看上去是它的指令集架构的简单实现,但是实际上现代处理器使用了非常复杂 的机制来加速程序的执行。

2.运行程序

初 始 时 ,shell 程序执行它的指令,等待我们输入一个命令。当我们在键盘上输入字符串 "./hello后 ,shell 程序将字符逐一读入寄存器,再把它存放到内存中

当我们在键盘上敲回车键时,shell 程序就知道我们已经结束了命令的输入。

然后 shell 执行一系列指令来加载可执行的hello 文件,这些指令将hello 目标文件中的代码 和数据从磁盘复制到主存。

数据包括最终会被输出的字符串 "hello,world\n"。 利用直接存储器存取(DMA, 将在第6章中讨论)技术,数据可以不通过处理器而直 接 从 磁 盘 到 达 主 存 。

一旦目标文件hello 中的代码和数据被加载到主存,处理器就开始执行hello 程序的 main程 序 中 的 机 器 语 言 指 令 。

这 些 指 令 将 "hello,world\n" 字 符 串 中 的 字 节 从 主 存复制到寄存器文件 , 再从寄存器文件中复制到显示设备 , 最终显示在屏幕上 。

2.高速缓存至关重要

1.数据复制带来的系统开销问题

这个简单的示例揭示了一个重要的问题,即系统花费了大量的时间把信息从一个地方挪到另一个地方。

hello 程序的机器指令最初是存放在磁盘上,当程序加载时,它们被复制到主存;

当处理器运行程序时,指令又从主存复制到处理器。

相似地,数据串 "hello,world/n" 开始时在磁盘上,然后被复制到主存,最后从主存上复制到显示设备。

从程序员的角度来看,这些复制就是开销,减慢了程序 "真正" 的工作。

因此,系统设计者的一个主要目标就是使这些复制操作尽可能快地完成。

2.存储设备的容量与速度矛盾

根据机械原理,较大的存储设备要比较小的存储设备运行得慢,而快速设备的造价远高于同类的低速设备。

比如说,一个典型系统上的磁盘驱动器可能比主存大 1000 倍,但是对处理器而言,从磁盘驱动器上读取一个字的时间开销要比从主存中读取的开销大 1000 万倍。

3.处理器与主存的速度差距持续扩大

类似地,一个典型的寄存器文件只存储几百字节的信息,而主存里可存放几十亿字节。

然而,处理器从寄存器文件中读数据比从主存中读取几乎要快 100 倍。

更麻烦的是,随着这些年半导体技术的进步,这种处理器与主存之间的差距还在持续增大。

加快处理器的运行速度比加快主存的运行速度要容易和便宜得多。

4.高速缓存存储器的解决方案

针对这种处理器与主存之间的差异,系统设计者采用了更小更快的存储设备,称为高速缓存存储器 (cache memory, 简称为 cache 或高速缓存),作为暂时的集结区域,存放处理器近期可能会需要的信息。

图 1-8 展示了一个典型系统中的高速缓存存储器。

位于处理器芯片上的 L1 高速缓存的容量可以达到数万字节,访问速度几乎和访问寄存器文件一样快。

一个容量为数十万到数百万字节的更大的 L2 高速缓存通过一条特殊的总线连接到处理器。

进程访问 L2 高速缓存的时间要比访问 L1 高速缓存的时间长 5 倍,但是这仍然比访问主存的时间快 5~10 倍。

5.高速缓存的实现与多级结构

L1 和 L2 高速缓存是用一种叫做静态随机访问存储器 (SRAM) 的硬件技术实现的。

比较新的、处理能力更强大的系统甚至有三级高速缓存:L1、L2 和 L3。

系统可以获得一个很大的存储器,同时访问速度也很快,原因是利用了高速缓存的局部性原理,即程序具有访问局部区域里的数据和代码的趋势。

通过让高速缓存里存放可能经常访问的数据,大部分的内存操作都能在快速的高速缓存中完成。

3.存储设备形成层次结构

处理器和一个较大较慢的设备(例如主存)之间插入一个更小更快的存储设备(例如 高速缓存)的想法已经成为一个普遍的观念。

实际上,每个计算机系统中的存储设备都被组织成了一个存储器层次结构,如图1-9所示。

在这个层次结构中,从上至下,设备的访 问速度越来越慢、容量越来越大,并且每字节的造价也越来越便宜。

寄存器文件在层次结 构中位于最顶部,也就是第0级或记为L0 。

存储器层次结构的主要思想是上一层的存储器作为低一层存储器的高速缓存。

因此, 寄存器文件就是L1 的高速缓存,L1 是 L2 的高速缓存,L2 是 L3 的高速缓存,L3 是主存 的高速缓存,而主存又是磁盘的高速缓存。

在某些具有分布式文件系统的网络系统中,本 地磁盘就是存储在其他系统中磁盘上的数据的高速缓存。

正如可以运用不同的高速缓存的知识来提高程序性能一样,程序员同样可以利用对整 个存储器层次结构的理解来提高程序性能。第6章将更详细地讨论这个问题。

4.系统之间利用网络通

实际上,现代系统经常通过网络和其他系统连接到一起。

从一个单独的系统来看,网络可视为一个 I/O 设备,如图1-14所示。

当系统从主存复制一串字节到网络适配器时,数据流经过网络 到达另一台机器,而不是比如说到达本地磁盘驱动器。

相似地,系统可以读取从其他机器 发送来的数据,并把数据复制到自己的主存。

回到 hello 示例,我们可以使用熟悉的 telnet 应用在一个远程主机上运行hello 程 序。

假设用本地主机上的 telnet 客户端连接远程主机上的 telnet 服务器。

在我们登录到远 程主机并运行shell 后,远端的shell 就在等待接收输入命令。

此后在远端运行hello 程序 包括如图1-15所示的五个基本步骤。

当我们在 telnet 客户端键入 "hello" 字符串并敲下回车键后,客户端软件就会将这 个字符串发送到 telnet 的服务器。

telnet 服务器从网络上接收到这个字符串后,会把它传 递给远端 shell 程序。

接下来,远端 shell 运行 hello 程序,并将输出行返回给telnet 服务 器。

最后,telnet 服务器通过网络把输出串转发给 telnet 客户端,客户端就将输出串输出 到 我 们 的 本 地 终 端 上 。

5.Amdahl 定律

Amdahl 定律是计算机系统里的一个重要规律,它帮我们回答一个问题:

我只优化了系统里的某一部分,整个系统能变快多少?

核心思想:

系统整体性能的提升,不只取决于你优化的那部分有多快,还取决于这部分在整个系统里占多大比例。

只优化一小部分,哪怕优化得再狠,整体提速也有限;

想让系统显著变快,必须优化占比很大的部分。

符号先搞懂

​:优化前整个系统跑程序需要的总时间

:你要优化的那部分,在总时间里占的比例

k:这部分被加速的倍数

​:优化后整个系统需要的总时间

S:系统整体的加速比

原来的总时间 分成两部分:

没优化的部分:占比 1−时间还是

优化的部分:原来占

现在变快 k 倍,时间变成

所以=(1-)+

算加速比 S

加速比 = 原来时间 ÷ 现在时间:

S==

举个例子,考虑这样一种情况,系统的某个部分初始耗时比例为60%(α=0.6),

其加速比 例因子为3(k=3) 。

则我们可以获得的加速比为1/[0.4+0.6/3]=1.67倍。

虽然我们对系统的 一个主要部分做出了重大改进,但是获得的系统加速比却明显小于这部分的加速比。

这就是 Amdahl 定律的主要观点------要想显著加速整个系统,必须提升全系统中相当大的部分的速度。

你把某一部分提速 3 倍,但整个系统只快了 1.67 倍
因为剩下 40% 的部分完全没优化,拖了后腿
这就是 Amdahl 定律的 "扎心" 结论:只优化局部,整体提升有限

:怎么表示性能提升?

推荐用「倍数」

比如

S=2.2

,就说 "性能提升 2.2×"(读作 "2.2 倍")

清晰、无歧义,一眼就知道变快了多少

不推荐用「百分比」

比如 "性能提升 120%",到底是100*(-)/

还是100*(-)/

定义模糊,大变化时也不好理解,不如倍数直观

. 核心结论

局部优化的天花板很低:哪怕你把某部分优化到无限快(k→∞),系统最大加速比也只有

想显著提速,必须优化占比大的部分:优化的部分在系统里占比越高,整体提速才越明显

用倍数表示性能最清晰:别用百分比,直接说 "X 倍"

第二章 信息表示和处理

1.信息存储

1.内存的基本单位与虚拟内存

核心知识点

最小可寻址单位:字节(byte)

大多数计算机用 8 位(bit) 组成一个字节(byte),这是内存里最小的可寻址单位。

你不能直接去访问内存里单独的某一位,只能以「字节」为单位读写。

虚拟内存:机器视角的大字节数组

机器级程序(比如编译后的二进制代码)会把内存看成一个非常大的字节数组,这个数组就叫虚拟

内存(virtual memory)。

每个字节都有一个唯一的数字标识,这个数字就是地址(address)。

所有可能的地址合在一起,就是虚拟地址空间(virtual address space)。

虚拟 vs 实际:概念性映像

虚拟地址空间只是给机器级程序看的「概念性映像」,并不是真实的物理内存。

实际实现(第 9 章会讲)是把 DRAM(动态随机访问存储器,就是我们常说的内存)、闪存、磁盘、特殊硬件 + 操作系统 组合起来,让程序觉得自己在用一个连续、统一的大字节数组。

2.程序对象与指针的本质

核心知识点

存储器空间的管理

编译器和运行时系统会把存储器空间划分成更易管理的单元,用来存放不同的程序对象(program object):

程序数据(比如变量、数组)

指令(比如函数代码)

控制信息(比如函数调用栈)

这些分配和管理的操作,完全在虚拟地址空间里完成,程序不需要关心物理内存的细节。

C 语言指针的本质

一个指针的值,就是某个存储块第一个字节的虚拟地址(不管它指向整数、结构体还是其他程序对象)。

C 编译器会把指针和它的类型信息绑定在一起(比如int *、struct A *)。

编译器会根据指针类型,生成不同的机器级代码,去访问指针指向位置的值(比如int类型要读 4 字节,char类型读 1 字节)。

机器级程序的特点

虽然 C 编译器维护着类型信息,但最终生成的机器级程序本身并不包含数据类型信息。

所以:每个程序对象都可以简单看成一个字节块,而程序本身就是一个字节序列(一串连续的字节)。

计算机以字节为最小内存单位,机器程序看到的是「虚拟内存」这个大字节数组,每个字节有唯一地址。

编译器在虚拟地址空间里管理程序对象,C 指针本质是字节地址,编译器靠类型信息生成访问代码,但最终机器码里没有类型,只有字节序列。

2.字数据大小

1.字长与虚拟地址空间
  1. 字长是什么?

字长:指针数据的标称大小,决定了虚拟地址的编码长度。

对于字长为 w位的机器:虚拟地址范围:0~

程序最多能访问 个字节的内存空间

  1. 向后兼容与编译选项

64 位机器可以运行 32 位程序(向后兼容)

编译指令示例:

gcc -m32 prog.c:编译为 32 位程序,可在 32/64 位机器运行

gcc -m64 prog.c:编译为 64 位程序,只能在 64 位机器运行

32 位程序 / 64 位程序:区别在于编译方式,不是运行的机器类型

个人理解:虚拟地址空间最大的意思并不是内存的最大 也不是磁盘的最大 而是一个进程的最大内存

2 位 vs 64 位字长迁移

32 位字长:虚拟地址空间最大为 4 GB()也就是

64 位字长:虚拟地址空间扩展到 16 EB(约)也就是

迁移历程:先在高端科学 / 数据库机器 → 台式机 / 笔记本 → 智能手机

向后兼容与编译选项

64 位机器可以运行 32 位程序(向后兼容)

编译指令示例:
gcc -m32 prog.c:编译为 32 位程序,可在 32/64 位机器运行
gcc -m64 prog.c:编译为 64 位程序,只能在 64 位机器运行

32 位程序 / 64 位程序:区别在于编译方式,不是运行的机器类型

3.C 语言数据类型的字节大小

char:固定 1 字节,可存字符或整数

int:通常固定 4 字节(即使 64 位系统)

long:32 位程序 4 字节,64 位程序 8 字节

指针:大小等于字长(32 位 = 4 字节,64 位 = 8 字节)

float(单精度浮点数):4 字节;double(双精度浮点数):8 字节

固定大小整数类型(ISO C99)

为避免依赖 "典型大小",引入 int32_t / uint32_t / int64_t / uint64_t

特点:字节数固定,不随编译器 / 机器变化

作用:让程序员精准控制数据表示,提升可移植性

char 的符号性特殊点

C 标准不保证 char 默认是有符号还是无符号

要确保 1 字节有符号数:必须写 signed char

实际开发中,多数编译器将 char 视为有符号数,但依赖这个行为不安全

C 语言允许关键字顺序 / 可选关键字省略,以下写法等价:

cpp 复制代码
unsigned long
unsigned long int
long unsigned
long unsigned int

可移植性要求

程序员应让程序在不同机器 / 编译器上都能运行,核心是对数据类型确切大小不敏感。

C 标准的范围约束

C 标准只规定了数据类型的数字范围下界,没有上界

这导致不同机器 / 编译器的字节大小可能不同

历史遗留问题

1980-2010 年:32 位机器 + 32 位程序是主流,很多代码假设 int=4字节、指针=4字节

64 位机器普及后:这些假设暴露问题

典型错误:用 int 存储指针

32 位机器:int=4字节,指针 = 4 字节 → 正常工作

64 位机器:int=4字节,指针 = 8 字节 → 数据截断,程序出错

4.字节和字长的区别

字节(Byte):是计算机里固定不变的最小存储单位,1 字节 = 8 位(bit)。

字长(Word Size):是 CPU 一次能处理 / 传输的二进制位数,由硬件架构决定(常见 32 位 / 64 位)。

字长决定虚拟地址空间大小字节

同时也决定了cpu一次能处理多少位的数据

Example:32位机器

决定了两件事 一件事cpu可以处理4字节(32/8)的东西

其次 虚拟地址空间的最大值是2^32字节

为什么地址空间是 字节,而不是 w 字节?

地址是 w位二进制,能表示

个不同的地址编号

每个地址编号对应 1 字节 的内存

所以总空间 = 个地址 × 1 字节 / 地址 = 字节

地址只和字长挂钩

比如32位机器 地址的大小就是32/8=4字节

64位机器地址的大小就是64/8=8字节

1 字节 = 8 位(bit)这个是固定的

为什么地址要和字长挂钩?

因为地址是 CPU 自己要用的,必须跟 CPU 的字长一样宽;
其他数据是给程序用的,CPU 可以分批处理,不需要跟字长一样。

地址是给 CPU 读内存用的,不是给程序逻辑用的。

CPU 从内存取数据时,要做这一步:

把 地址 放到地址总线上 → 找到内存位置

地址总线有多宽,地址就必须是多少位。

地址总线宽度 = CPU 字长。

所以:

32 位 CPU → 地址总线 32 位 → 地址必须 32 位

64 位 CPU → 地址总线 64 位 → 地址必须 64 位

地址必须一次性完整传给 CPU,不能拆两半。

所以地址必须跟字长一样宽。

相关推荐
-To be number.wan3 天前
计算机组成原理-计算机系统概述复盘
学习·计算机组成原理
杰 .13 天前
计算机组成原理入门理解——理解这门课的关键思路
计算机组成原理
myloveasuka1 个月前
寻址方式笔记
汇编·笔记·计算机组成原理
myloveasuka1 个月前
指令格式举例
汇编·笔记·计算机组成原理
myloveasuka1 个月前
定点运算---加减法运算
笔记·计算机组成原理
剪一朵云爱着2 个月前
计算机组成原理 (四)计算机性能
408·计算机组成原理·计算机基础
剪一朵云爱着2 个月前
计算机组成原理(一)计算机概述
408·计算机组成原理·计算机基础·四大件
myloveasuka2 个月前
分离指令缓存(I-Cache)和数据缓存(D-Cache)的原因
笔记·缓存·计算机组成原理·硬件
啊阿狸不会拉杆2 个月前
《计算机操作系统》第十二章 - 保护和安全
开发语言·网络·c++·算法·安全·计算机组成原理·计算机操作系统