第一章 计算机系统漫游
了解编译系统如何工作是大有益处的
有一些重要的原因促使程序员必须知道编译系统是如何工作的。
- 优化程序性能。现代编译器都是成熟的工具,通常可以生成很好的代码。作为程序员,我们无须为了写出高效代码而去了解编译器的内部工作。但是,为了在C程序中做出更好的编码选择,我们确实需要了解一些机器代码以及编译器将不同的C语言转化为机器码的方式。比如,一个switch语句是否总是比一系列的if-else语句高效的多?一个函数的调用的开销有多大?while循环比for循环更有效吗?指针引用比数组索引更有效吗?为什么将循环求和的结果放到一个本地变量中,会比将其放到一个通过引用传递过来的参数中,运行起来快很多呢?
- 理解链接时出现的错误。
- 避免安全漏洞。
系统的硬件组成
- 总线。贯穿整个系统的是一组电子管,称作总线,它携带信息字节并负责在各个部件间传递。通常总线被设计成传送定长的字节块,也就是字(word)。字中的字节数(字长)是一个基本的系统参数,各个系统中都不尽相同。现在大多数机器字长要么是4个字节(32位),要么是8字节(64位)。
- I/O设备。I/O(输入/输出)设备是系统与外部世界的联系通道。每个I/O设备都通过一个控制器或适配器与I/O总线相连。
- 主存。主存是一个临时存储设备,在执行程序时,用来存放程序与程序处理的数据。从物理上来说,主存是由一组**动态随机存取存储器(DRAM)**芯片组成的。从逻辑上来说,存储器是一个线性的字节数组,每个字节都有其唯一的地址(数组索引),这些地址都是从零开始的。
- 处理器。中央处理单元(CPU),简称处理器,是解释(或执行)存储在主存中指令的引擎。处理器的核心是一个大小为一个字的存储设备(或寄存器),称为程序计数器(PC)。在任何时刻,PC都指向主存中的某条机器语言指令(即含有该指令的地址)。
高速缓存至关重要
根据机械原理,较大的存储设备要比较小的存储设备运行的慢,而快速设备的造价远高于同类的低速设备。比如说,一个典型系统上的磁盘驱动器可能比主存大1000倍,但是对处理器而言,从磁盘驱动器读取一个字的时间花销要比从主存读取的开销大1000万倍。更麻烦的是,随着这些年半导体技术的进步,这种处理器与主存之间的差距还在增大。加快处理器的运行速度比加快主存的运行速度要容易和便宜的多。
操作系统管理硬件
我们可以把操作系统看作是应用程序和硬件之间插入的一层软件。所有的应用程序对硬件的操作尝试都必须通过操作系统。
操作系统有两个基本功能:
- 防止硬件被失控的应用程序滥用
- 向应用程序提供简单一致的机制来控制复杂而又通常大不相同的低级硬件设备。操作系统通过几个基本的抽象概念(进程、虚拟内存和文件)来实现这两个功能。文件是对I/O设备的抽象表示,虚拟内存是对主存和磁盘I/O设备的抽象表示,进程则是对处理器、主存和I/O设备的抽象表示。
进程
进程是计算机科学中最重要和最成功的概念之一。进程是操作系统对一个正在运行的程序的一种抽象。在一个系统上可以同时运行多个进程,而每个进程都好像在独占地使用硬件。而并发运行,则是说一个进程指令和另一个进程指令是交错执行的。在大多数系统中,需要运行的进程数是多于可以运行它们的CPU个数的。传统系统在同一时刻只能执行一个程序,而先进的多核处理器同时能够执行多个程序。无论是在单核还是多核系统中,一个CPU看上去都像是并发地执行多个进程,这是通过处理器在进程间切换来实现的。操作系统实现这种交错执行的机制称为上下文切换。
从一个进程到另一个进程的转换是由操作系统内核(Kernel)管理的。内核是操作系统代码常驻主存的部分。当应用程序需要操作系统的某些操作时,比如读写文件,它就执行一条特殊的系统调用(system call)指令,将控制权传递给内核。然后内核执行被请求的程序并返回应用程序。注意,内核并不是一个独立的进程。相反,它是系统管理全部进程所用代码和数据结构的集合。
线程
尽管通常我们认为一个进程只有单一的控制流,但是在现代系统中,一个进程实际上可以由多个称为线程的执行单元组成,每个线程都运行在进程的上下文中,并共享同样的代码和全局数据。由于网络服务器中对并行处理的需求,线程越来重要的编程模型,因为多线程之间比多进程之间更容易共享数据,也因为线程一般来说都比进程更高效。
虚拟内存
虚拟内存是一个抽象概念,它为每一个进程提供一个假象,即每个进程都在独占地使用主存。每个进程看到的主存都是一致的,称为虚拟地址空间。
- 堆。代码和数据区后紧跟的是运行时堆。代码和数据区在进程一开始运行时就被指定来大小。
- 共享库。大约在地址中间部分是一块用来存放像C标准库和数学库这样的共享库的代码和数据的区域。
- 栈。位于虚拟地址空间顶部的是用户栈,编译器用它来实现函数调用。
- 内核虚拟内存。地址空间顶部区域是为内核保留的。不允许应用程序读写这个区域的内容或者直接调用内核定义的函数。
文件
文件就是字节序列,仅此而已。每个I/O设备,包括磁盘、键盘、显示器甚至是网络,都可以看成是文件。
并发与并行
数字计算机的整个历史中,有两个需求是驱动进步的持续动力:一个是我们想要计算机做的更多,另一个是我们想要计算机运行的更快。当处理器能够同时做更多的事情时,这两个因素都会改进。我们用的术语并发(concurrency)是一个通用的概念,指一个同时具有多个活动的系统。而术语并行(parallelism)指的是用并发来使一个系统运行的更快。
线程级并发
使用线程,我们甚至能够在一个进程中执行多个控制流。传统意义上,这种并发执行只是模拟出来的,是通过一台计算机在它正在执行的进程间快速切换来实现的。
多核处理器是将多个CPU(称为核)集成到一个集成电路芯片上。工业界的专家预言他们能够将几十个、最终会是上百个核做到一个芯片上。
超线程,通常被称为多线程(multi-threading),是一项允许一个CPU执行多个控制流的技术。它涉及CPU某些硬件有多个备份,比如程序计数器和寄存器文件,而其他的硬件部分只有一份,比如执行浮点运算的单元。举例来说,Intel Core i7处理器可以让每个核执行两个线程,所以一个4核的系统实际上可以并行地执行8个线程。
计算机系统中抽象的重要性
虚拟机,它提供对计算机的抽象,包括操作系统、处理器核程序。虚拟机的思想是IBM在20世纪60年代提出来的,但是最近才显示出其管理计算机方式上的优势,因为一些计算机必须能够运行为不同的操作系统(例如,Microsoft Windows、macOS和Linux)或同一操作系统的不同版本设计的程序。