Skip to content

计算机系统漫游

c
#include <stdio.h>
int main()
{
    printf("hello, world\n");
    return 0;
}

1.1 信息就是位 + 上下文

hello 程序的生命周期是从一个源程序(或者说源文件)开始的,即程序员通过编辑器创建并保存的文本文件,文件名是 hello.c。源程序实际上就是一个由值 0 和 1 组成的位(又称为比特)序列,8 个位被组织成一组,称为字节。每个字节表示程序中的某些文本字符。

1.2 程序被其他程序翻译成不同的格式

hello 程序的生命周期是从一个高级 C 语言程序开始的,因为这种形式能够被人读懂。然而,为了在系统上运行 hello.c 程序,每条 C 语句都必须被其他程序转化为一系列的低级机器语言指令。然后这些指令按照一种称为可执行目标程序的格式打好包,并以二进制磁盘文件的形式存放起来。目标程序也称为可执行目标文件。

在 Unix 系统上,从源文件到目标文件的转化是由编译器驱动程序完成的∶

bash
linux> gcc -o hello hello.c

在这里,GCC 编译器驱动程序读取源程序文件 hello.c,并把它翻译成一个可执行目标文件 hello。这个翻译过程可分为四个阶段完成

  • 预处理阶段。预处理器(cpp)根据以字符 # 开头的命令,修改原始的 C 程序。比如 hello.c 中第 1 行的#include <stdio.h>命令告诉预处理器读取系统头文件 stdio.h 的内容,并把它直接插入程序文本中。结果就得到了另一个 C 程序,通常是以 .i 作为文件扩展名。
  • 编译阶段。编译器(ccl)将文本文件 hello.i 翻译成文本文件 hello.s,它包含一个汇编语言程序。
  • 汇编阶段。接下来,汇编器(as)将 hello.s 翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序(relocatable object program)的格式
  • 链接阶段。请注意,hello 程序调用了 printf 函数,它是每个 C 编译器都提供的标准 C 库中的一个函数。printf 函数存在于一个名为 printf.o 的单独的预编译好了的目标文件中,而这个文件必须以某种方式合并到我们的 hello.o 程序中。链接器(ld)就负责处理这种合并。结果就得到 hello 文件,它是一个可执行目标文件(或者简称为可执行文件),可以被加载到内存中,由系统执行。

1.3 了解编译系统如何工作是大有益处的

  • 优化程序性能
  • 理解链接时出现的错误。为什么有些链接错误直到运行时才会出现
  • 避免安全漏洞。理解数据和控制信息存储在程序栈上的方式会引起的后果

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

此刻,hello.c 源程序已经被编译系统翻译成了可执行目标文件 hello,并被存放在磁盘上。要想在 Unix 系统上运行该可执行文件,我们将它的文件名输入到称为 shell 的应用程序中

bash
linux> ./hello
hello, world
linux>

shell

shell 是一个命令行解释器,它输出一个提示符,等待输入一个命令行,然后执行这个命令。如果该命令行的第一个单词不是一个内置的 shell 命令,那么 shell 就会假设这是一个可执行文件的名字,它将加载并运行这个文件。

1.5 系统的硬件组成

  • 处理器

中央处理单元(CPU),简称处理器,是解释(或执行)存储在主存中指令的引擎。处理器的核心是一个大小为一个字的存储设备(或寄存器),称为程序计数器(PC)。在任何时刻,PC 都指向主存中的某条机器语言指令(即含有该条指令的地址)。

  • 主存

主存是一个临时存储设备,在处理器执行程序时,用来存放程序和程序处理的数据。从物理上来说,主存是由一组动态随机存取存储器(DRAM)芯片组成的。

  • I/O 设备

I/O(输入/输出)设备是系统与外部世界的联系通道。我们的示例系统包括四个 I/O 设备∶作为用户输入的键盘和鼠标,作为用户输出的显示器,以及用于长期存储数据和程序的磁盘驱动器。

  • 总线

贯穿整个系统的是一组电子管道,称作总线,它携带信息字节并负责在各个部件间传递。通常总线被设计成传送定长的字节块,也就是字(word)。字中的字节数(即字长)是一个基本的系统参数,各个系统中都不尽相同。

  • 运行 hello 程序

2

1.6 高速缓存至关重要

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

一个典型的寄存器文件只存储几百字节的信息,而主存里可存放几十亿字节。然而,处理器从寄存器文件中读数据比从主存中读取几乎要快 100 倍。

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

1.7 存储设备形成层次结构

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

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

1

存储器层次结构的主要思想是上一层的存储器作为低一层存储器的高速缓存。因此,寄存器文件就是 L1 的高速缓存,L1 是 L2 的高速缓存,L2 是 L3 的高速缓存,L3 是主存的高速缓存,而主存又是磁盘的高速缓存。在某些具有分布式文件系统的网络系统中,本地磁盘就是存储在其他系统中磁盘上的数据的高速缓存。

1.8 操作系统管理硬件

当 shell 加载和运行 hello 程序时,以及 hello 程序输出自己的消息时,shell 和 hello 程序都没有直接访问键盘、显示器、磁盘或者主存。取而代之的是,它们依靠操作系统提供的服务。

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

  • 进程

进程是操作系统对一个正在运行的程序的一种抽象。在一个系统上可以同时运行多个进程,而每个进程都好像在独占地使用硬件。而并发运行,则是说一个进程的指令和另一个进程的指令是交错执行的。在大多数系统中,需要运行的进程数是多于可以运行它们的 CPU 个数的。传统系统在一个时刻只能执行一个程序,而先进的多核处理器同时能够执行多个程序。

  • 线程

尽管通常我们认为一个进程只有单一的控制流,但是在现代系统中,一个进程实际上可以由多个称为线程的执行单元组成,每个线程都运行在进程的上下文中,并共享同样的代码和全局数据。由于网络服务器中对并行处理的需求,线程成为越来越重要的编程模型,因为多线程之间比多进程之间更容易共享数据,也因为线程一般来说都比进程更高效。

  • 虚拟内存

虚拟内存是一个抽象概念,它为每个进程提供了一个假象,即每个进程都在独占地使用主存。每个进程看到的内存都是一致的,称为虚拟地址空间。

  • 文件

文件就是字节序列,仅此而已。每个I/O设备,包括磁盘、键盘、显示器,甚至网络,都可以看成是文件。系统中的所有输入输出都是通过使用一小组称为 Unix I/O 的系统函数调用读写文件来实现的。

文件这个简单而精致的概念是非常强大的,因为它向应用程序提供了一个统一的视图,来看待系统中可能含有的所有各式各样的 I/O 设备。

1.9 系统之间利用网络通信

系统漫游至此,我们一直是把系统视为一个孤立的硬件和软件的集合体。实际上,现代系统经常通过网络和其他系统连接到一起。

当系统从主存复制一串字节到网络适配器时,数据流经过网络到达另一台机器,而不是比如说到达本地磁盘驱动器。相似地,系统可以读取从其他机器发送来的数据,并把数据复制到自己的主存。

1.10 重要主题

  • Amdahl 定律

该定律的主要思想是,当我们对系统的某个部分加速时,其对系统整体性能的影响取决于该部分的重要性和加速程度。

  • 并发和并行

数字计算机的整个历史中,有两个需求是驱动进步的持续动力:一个是我们想要计算机做得更多,另一个是我们想要计算机运行得更快。

  • 计算机系统中抽象的重要性

抽象的使用是计算机科学中最为重要的概念之一。例如,为一组函数规定一个简单的应用程序接口(API)就是一个很好的编程习惯,程序员无须了解它内部的工作便可以使用这些代码。不同的编程语言提供不同形式和等级的抽象支持,例如 Java 类的声明和C语言的函数原型。

在学习操作系统时,我们介绍了三个抽象:文件是对 I/O 设备的抽象,虚拟内存是对程序存储器的抽象,而进程是对一个正在运行的程序的抽象。我们再增加一个新的抽象∶ 虚拟机,它提供对整个计算机的抽象,包括操作系统、处理器和程序。

1.11 小结

计算机系统是由硬件和系统软件组成的,它们共同协作以运行应用程序。

计算机内部的信息被表示为一组组的位,它们依据上下文有不同的解释方式。

程序被其他程序翻译成不同的形式,开始时是 ASCII 文本,然后被编译器和链接器翻译成二进制可执行文件。

处理器读取并解释存放在主存里的二进制指令。

因为计算机花费了大量的时间在内存、I/O 设备和 CPU 寄存器之间复制数据,所以将系统中的存储设备划分成层次结构

操作系统内核是应用程序和硬件之间的媒介。它提供三个基本的抽象∶1)文件是对 I/O 设备的抽象;2)虚拟内存是对主存和磁盘的抽象;3)进程是处理器、主存和 I/O 设备的抽象。

最后,网络提供了计算机系统之间通信的手段。从特殊系统的角度来看,网络就是一种 I/O 设备。