PS:本文都是依据CSAPP来写的。
本章将通过研究 "hello, world" 这个简单程序的声明周期,介绍计算机系统的主要概念和主题。
//hello.c
#include <stdio.h>
int main() {
printf("hello, world\\n");
return 0;
}
编译系统
为了在系统上运行 hello.c 程序,我们需要将其转化为可执行目标文件,在 Unix 系统上,我们通过下述指令完成转化(由GCC编译器驱动程序完成),
linux> gcc -o hello hello.c
编译hello程序
这个翻译过程可分为四个阶段完成,而执行这四个阶段的程序,一起构成了编译系统(如下)。

文本文件:像 hello.c 这样只由 ASCLL 字符构成的文件。
二进制文件:所有其它文件。
四个阶段:
预处理阶段。预处理器(cpp)根据以字符#开头的命令,修改原始的C程序(例如,#include <stdio.h> 就是告诉预处理器读取系统头文件stdio.h的内容,并把它直接插入程序文本中),通常得到以 .i 作为文件扩展名。
编译阶段。编译器(ccl)将文本文件 hello.i 翻译成文本文件 hello.s,得到汇编语言程序。
汇编阶段。汇编器(as)将 hello.s 翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序的格式,得到 hello.o 文件(二进制文件)。
链接阶段。(hello程序调用了 printf 函数,它是每个C编译器都提供的标准C库中的一个函数)printf 函数存在于一个名为 printf.o 的单独的预编译好了的目标文件中,我们需要将这个文件合并到hello.o 程序中,而链接器(ld)就起到了负责合并的作用,得到 hello 可执行目标文件。
了解编译系统是怎么工作带来的益处:
优化程序性能。我们无需为了写出高效代码而去了解编译器的内部工作,但是我们需要做出更好的编码选择,比如,一个 switch 语句是否总是比一系列的 if-else 语句高效得多。
理解链接时出现的错误。一些最令人困扰的程序错误往往都与链接器操作有关。
避免安全漏洞。缓冲区溢出错误是造成大多数网络和 Internet 服务器上安全漏洞的主要原因。存在这些错误是因为很少有程序员能够理解需要限制从不受信任的源接收数据的数量和格式。
运行系统
hello.c 源程序已经被编译系统翻译成了可执行目标文件 hello,下面通过 shell(命令行解释器)运行该可执行文件。
linux> ./hello
hello, world
linux>
系统的硬件组成

1.总线。贯穿整个系统的一组电子管道,携带信息字节并负责在各个部件间传递。通常总线被设计成传送定长的字节块(字),字中的字节数即为字长。
2.I/O设备。系统与外部世界的联系通道,每个I/O设备都通过一个控制器或适配器与I/O总线相连。
控制器和适配器之间的区别主要在于它们的封装方式,控制器是主板上的芯片组,而适配器则是一块插在主板插槽上的卡。
3.主存。是一个临时储存设备,在处理器执行程序时,用来存放程序和程序处理的数据。从物理上来说,主存是由一组动态随机存取器(DRAM)芯片组成的;逻辑上来说,主存是一个线性的字节数组。
4.处理器。中央处理单元(CPU),是解释(或执行)存储在主存中指令的引擎。处理器的核心是一个大小为一个字的存储设备(或寄存器),称为程序计数器(PC),指向主存中的某条机器语言指令。
处理器的指令集架构(从PC指向的内存处读取指令,解释指令中的位,执行该指令的简单操作,然后更新PC,使其指向下一条指令)描述的是每条机器代码指令的效果;
而微体系结构描述的是处理器实际上是如何实现的。
运行hello程序(整体描述)
1.我们输入指令
linux> ./hello
shell 程序将字符逐一读入寄存器,再把它放到内存中。

2.我们在键盘上敲回车键时,结束命令输入,shell 执行一系列指令来加载可执行的 hello 文件,这些指令将 hello 目标文件中的代码和数据从磁盘复制到主存。
利用直接存储器存取(DMA)技术,数据可不通过处理器而直接从磁盘到达主存。

3.一旦目标文件 hello 中的代码和数据被加载到主存,处理器就开始执行 hello 程序的 main 程序中的机器语言指令。
这些指令将 "hello, world\\n" 字符串中的字节从主存复制到寄存器文件,再从寄存器文件中复制到显示设备,最终显示在屏幕上。

简单来说,hello 程序最初是在磁盘上的,程序加载时,它们被复制到主存;当处理器运行程序时,指令又从主存复制到处理器。
由于处理器从寄存器文件中读取数据的速度远远大于从主存中读取的速度,寄存器容量小,造价高;内存容量较大,但是提升速度的开销也很大。
于是系统设计者就采用了高速缓存存储器(cache),存放可能经常访问的数据,大部分的内存操作都能在快速的高速缓存中完成。

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

操作系统
可以把操作系统看成是应用程序和硬件之间插入的一层软件,所有应用程序对硬件的操作尝试必须通过操作系统。

操作系统两个基本功能(通过进程、虚拟内存和文件这几个抽象概念来实现):
1)防止硬件被失控的应用程序滥用;
2)向应用程序提供简单一致的机制来控制复杂而又通常大不相同的低级硬件设备。

相关抽象概念(四个)
进程
进程是操作系统对一个正在运行的程序的一种抽象(是对处理器、主存和I/O设备的抽象表示)。
在一个系统上可以同时运行多个进程,而每个进程看上去都好像在独占地使用硬件。而并发运行,则是说一个进程的指令和另一个进程的指令是交错执行的(操作系统实现这种交错执行的机制称为上下文切换)。
上下文:操作系统保持跟踪进程运行所需的所有状态信息。这种状态,也就是上下文,比如,PC和寄存器文件的当前值,以及主存的内容。
上下文切换:操作系统决定要把控制权从当前进程转移到某个新进程时,就会进行上下文切换,即保存当前进程的上下文、恢复新进程的上下文,然后将控制权传递到新进程。新进程就会从它上次停止的地方开始。
上下文切换是由操作系统内核管理的,内核是操作系统代码常驻主存的部分,不是一个独立的进程,相反,它是系统管理全部进程所用代码和数据结构的集合。
shell进程与hello进程的并发运行。

线程
在现代操作系统中,一个进程实际上可以由多个称为线程的执行单元组成,每个线程都运行在进程的上下文中,并共享同样的代码和全局数据。
虚拟内存
虚拟内存是一个抽象概念,为每个进程提供都在独占地使用主存的假象。每个进程看到的内存都是一致的,称为虚拟地址空间。

每个进程看到的虚拟地址空间由大量准确定义的区构成,每个区都有专门的功能。
从最低的地址开始,逐步向上:
- 程序代码和数据:直接按照可执行目标文件的内容初始化。
- 堆:动态扩展和收缩大小。
- 共享库:存放像C标准库和数学库这要的共享库的代码和数据的区域。
- 栈:实现函数调用。
- 内核虚拟内存:为内核保留,不允许应用程序读写这个区域的内容。
文件
文件就是字节序列。每个I/O设备,包括磁盘、键盘、显示器,甚至网络,都可以看成是文件。(Linux下,一切皆文件)
系统中的所有输入输出都是通过使用一小组称为 Unix I/O 的系统函数调用读写文件来实现的。
网络系统
从一个单独的系统来看,网络可视为一个I/O设备,当系统从主存复制一串字节到网络适配器时,数据流经过网络到达另一台机器。

使用telnet应用在一个远程主机上运行hello程序,完成客户端与服务器之间的交互。

重要概念(三个)
Amdahl定律
当我们对系统的某个部分加速时,其对系统整体性能的影响取决于该部分的重要性和加速程度。(想要显著加速整个系统,必须提升全系统中相当大的部分的速度)
若系统执行某应用程序需要时间为
,假设系统某部分所需执行时间与改时间的比例为
,而该部分性能提升比例为
。
![T_{new} = (1-\\alpha )T_{old} + (\\alpha T_{old})/k = T_{old} [(1-\\alpha) + \\alpha / k]](https://www.171host.com/wp-content/uploads/2026/02/20260223075548-699c0804672a2.png)
加速比
为

并发和并行
并发:一个同时具有多个活动的系统。
并行:用并发来使一个系统运行得更快。
1.线程级并发
传统意义上的并发只是模拟出来的(单核处理器),是在执行的进程间快速切换来实现的。
现代的多处理器系统(多核处理器与超线程技术),
下面是多核处理器的组织结构,

再加上超线程(同时多线程),一个CPU执行多个控制流,让多核处理器中的每个核执行多个线程。
通过减少在执行多个任务时并发的需要;使应用程序运行得更快,从而提高系统性能。
2.指令级并行
在较低的抽象层次上,现代处理器可以同时执行多条指令的属性称为指令级并行(流水线的使用,一条指令划分的步骤可以并行地操作)。
如果一个处理器可以达到一个周期一条指令更快的执行速率,就称之为超标量。
3.单指令、多数据并行
在最低层次上,许多现代处理器拥有特殊的硬件,允许一条指令产生多个可以并行执行的操作,这种方式称为单指令、多数据。
抽象
计算机系统提供的一些抽象。计算机系统中的一个重要主题就是提供不同层次的抽象表示,来隐藏实际实现的复杂性。

小结
计算机系统是由硬件和系统软件组成的,它们共同协作以运行应用程序。计算机内部的信息被表示为一组组的位,它们依据上下文有不同的解释方式。程序被其他程序翻译成不同的形式,开始时是ASCLL文本,然后被编译器和连接器翻译成二进制可执行文件。
处理器读取并解释存放在主存里的二进制指令。因为计算机花费了大量的时间在内存、I/O设备和CPU寄存器之间复制数据,所以将系统中的存储设备划分成层次结构——CPU寄存器在顶部,接着是多层的硬件高速缓存存储器、DRAM主存和磁盘存储器。在层次模型中,位于更高层的存储设备比低层的存储设备要更快,单位比特造价也更高。层次结构中较高层次的存储设备可以作为较低层次设备的高速缓存。通过理解和运用这种存储层次结构的只是,程序员可以优化C程序的性能。
操作系统内核是应用程序和硬件之间的媒介。它提供三个基本的抽象:1)文件是对I/O设备的抽象。2)虚拟内存是对主存和磁盘的抽象。3)进程是处理器、主存和I/O设备的抽象。
最后,网络提供了计算机系统之间通信的手段。从特殊系统的角度来看,网络就是一种I/O设备。

