第一章——计算机系统漫游

1.1 信息的表示方式

比特+上下文:
在计算机系统中,所有信息都被表示为连续的比特串。读取信息时需根据不同的上下文,来获取对应比特串的特定含义。

1.2 程序的翻译过程

以C语言为例,为了能够在系统上运行高级程序,每条C语句都必须被转化为一系列的机器语言指令,也就是从.c文件到可执行目标文件(Windows系统中的.exe文件或者Linux中的ELF文件)的转换。
编译系统
整个翻译过程可以分为四个阶段:
预处理:预处理器将.c文本文件中以字符#开头的命令替换成对应的内容,得到另一个程序文本文件,通常拓展名为.i;
编译:编译器将hello.i文本文件编译成hello.s文本文件,它包含了一个汇编语言程序;
汇编:将hello.s文件翻译成机器语言指令,并将指令打包成一种可重定位目标程序的格式,保存在hello.o文件中;
链接:hello程序调用了printf函数,该函数是C标准库中的一个函数,链接就是将名为printf.o的单独的目标文件合并到hello.o程序中,最终得到可执行目标文件hello(简称可执行文件)。

1.3 了解编译系统工作的益处

● 优化程序性能
● 理解链接时出现的错误
● 避免安全漏洞

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

1.4.1 计算机系统硬件组成

典型计算机系统硬件组成
总线
总线是贯穿系统的一组电子管道,信息通过总线得以在各部件间传递,通常被设计成传送固定长度的字节块,即字(word),在大多数计算机中一个字长为4字节(32位)或者8字节(64位)。
主存储器
主存是一个临时存储设备,在处理器运行时用以存放程序指令和程序待处理的数据。从物理上而言,主存由一组动态随机存取存储器(DRAM)芯片组成;从逻辑上而言,主存可视为一个线性的字节数组,每个字节都有唯一的地址(数组索引)。
I/O设备
I/O设备是系统与外部世界的交互通道,包括输入设备和输出设备。每个设备都通过一个控制器或适配器与I/O总线相连。控制器可以简单理解为设备本身或者系统主板上的芯片组;适配器则是插在主板插槽上的卡。
处理器
中央处理器CPU是解释或执行存储在主存中指令的引擎。处理器的核心为一个大小为一个字的寄存器,称为程序计数器(Program Count,PC),在任意时刻,PC都指向主存中某条机器语言指令。处理器的操作PC、寄存器文件、算术逻辑单元(ALU)进行。
PC:程序计数器,拥有一个字(4字节/8字节)的大小,指向主存中待执行指令的存储地址;
寄存器文件:是一个小的存储设备,由一些单个字长大小寄存器组成,每个寄存器有唯一的标识;
ALU:算术逻辑单元根据指令对数据进行运算并更新地址。
CPU的主要操作包括:
加载:将主存中一个字节/字复制到寄存器中,覆盖掉原有内容;
存储:将寄存器中一个字节/字复制到主存的某个地址中,覆盖掉原有内容;
操作:将两个寄存器中的内容复制到ALU中,ALU对两个字进行算术运算,并将结果存放到其中一个寄存器中,覆盖该寄存器中的原有内容;
跳转:从主存中的指令中选取一个字,将其复制到PC中,覆盖原内容。

1.4.2 运行hello程序

从键盘输入hello命令
首先,用户在通过键盘输入命令行“./hello”后。shell程序将字符逐一读入寄存器,再将其存入主存中。
磁盘加载可执行文件到主存
当敲下回车时,shell程序知道我们已经输入完毕。然后shell执行一系列指令来加载可执行程序hello,包括hello目标文件中的代码和数据,将其从磁盘中复制到主存中。这里利用直接存储器存取技术(DMA),数据可以不通过CPU直接从磁盘到达主存。
输出字符串到显示器
一旦目标文件hello中的代码和数据被加载到主存中,CPU就开始执行hello程序main函数中的机器语言指令。这些指令将“hello, world\n”字符的字节信息从主存复制到寄存器文件,再从寄存器文件复制到显示设备,最终显示在屏幕上。

1.5 高速缓存的重要性

计算机系统花费了大量的时间把信息从一个位置复制到另一个位置,这些复制就是开销,减慢了程序“真正”运行的时间。根据机械原理,较大的存储设备运行速度要慢于较小的存储设备,而快速设备造假远高于同类型低速设备。
一个典型系统中,计算机磁盘可能比主存大1000倍,但是对CPU而言,从磁盘上读取一个字的时间开销要比从主存读取的开销大1000万倍。类似地,一个寄存器文件只存储几百字节的信息,主存可以存放几十亿字节。但是CPU从寄存器文件读取数据要比从主存读取数据快100倍。随着半导体技术的发展,这种差异还在增大,因为相比于提高主存运行速度而言,提升CPU的运行速度更加容易和经济。
高速缓存存储器
针对CPU与主存之间的差异,系统设计者采用了更小更快的设备作为信息的暂时集结区域,称为高速缓存存储器(cache memory)。位于CPU中的L1高速缓存容量可达数万字节,访问速度与寄存器文件相当;L2高速缓存的容量为数十万到数百万字节不等,通过一条特殊的总线连接到CPU,访问速度比L1高速缓存慢5倍。L1和L2高速缓存是使用一种叫做静态随机访问存储器(SRAM) 的硬件技术实现的。新的性能强大的系统甚至有是哪集高速缓存:L1、L2和L3。系统将经常访问的数据存放在高速缓存中,大部分的操作都可以快速进行。

1.6 存储设备的层次结构

每个计算机系统的存储设备都被组成了一个存储器层次结构,在该结构中,自上至下,存储设备的容量越来越大,访问速度越来越慢,每字节造价也越来越便宜。存储器层次结构的思想是上一层的存储器作为低一层存储器的高速缓存,如寄存器中保存来自L1高速缓存中的字,以此类推。
存储器层次结构

1.7 操作系统管理硬件

当shell程序加载运行hello程序时,shell和hello程序都没有直接访问键盘、鼠标、磁盘或者主存等硬件设备。取而代之的是这些应用程序通过操作系统来对硬件实现访问,操作系统可以视为夹在应用程序与计算机硬件设备之间的一层软件,所有应用程序对硬件的操作尝试都必须通过操作系统完成。
计算机系统分层示意图
操作系统的两大功能:(1)防止硬件被失控的应用程序滥用;(2)向应用程序提供简单一致的机制来控制复杂且不尽相同的低级硬件设备。操作系统通过进程虚拟内存文件等三个基本的抽象概念来实现这两大功能。文件是对I/O设备的抽象表示,虚拟内存是对主存和磁盘I/O设备的抽象表示;进程则是对处理器、主存和I/O设备的抽象表示。
操作系统提供的抽象服务

1.7.1 进程

进程是一种假象,像hello程序在系统中进行运行时,好像系统上只有这个程序在运行,其独占地使用处理器、主存、I/O设备。这种假象就是通过进程实现的。
进程是操作系统对于一个正在运行的程序的抽象。在一个系统上可以同时运行多个进程,每个进程好像都在独占地使用硬件。并发运行是指一个进程的指令和另一个进程的指令是交替执行的。大多数系统中,需要运行的进程数是多于CPU个数的,每个CPU看起来都是在并发执行多个程序,这是处理器通过在进程间来回切换实现的。操作系统实现这种交替执行的机制称为上下文切换
操作系统在进行上下文切换时会保持跟踪进程运行所需的所有状态信息,即上下文信息。对于单CPU系统,其在任意时刻都只能执行一个进程。当系统决定要把控制权从当前进程转移到某个新进程时,就会保存当前进程的上下文、加载新进程的上下文,然后将控制权转移至新进程。
进程上下文切换
从一个进程到另一个进程的转换是由操作系统内核(kernel)管理的。内核是操作系统代码常驻主存的部分。内核不是进程,是系统管理进程的代码和数据的集合。

1.7.2 线程

现代系统中,一个进程可以由多个称为线程的执行单元组成,每个线程都运行在进程的上下文中,共享代码和全局数据。相比于进程,线程间更容易共享数据、更高效。当系统拥有多个CPU(核)时,多线程可以加速程序运行速度。

1.7.3 虚拟内存

虚拟内存为每个进程都提供了一个假象,即每个进程都是独占使用主存的。每个进程看到的都是地址从0开始的内存,称为虚拟内存空间。Linux系统的虚拟内存空间如图所示,地址从下至上增大。
虚拟内存分布
按照地址从小到大,各个部分为:
程序代码与数据:第一个区域中首先存放只可读的程序代码与数据,然后存放全局变量。该区域中的内容是根据可执行目标文件初始化的,初始化后大小固定不变。
:当程序中调用malloc/new这类C++标准库时,堆区可以动态的扩展收缩。
共享库:用于存放C++标准库、数学库等共享库的代码与数据。hello示例中的printf函数就存放在共享库区域。
:栈区由系统管理,编译器用它实现函数调用。当调用一个函数时就将其压入栈中,该函数执行完毕后就执行出栈操作。
内核虚拟内存:该区域是保留给操作系统中的代码与数据的,不允许应用程序读写该区域的内容或者调用内核代码函数。

1.7.4 文件

文件就是字节序列。每个I/O设备,如磁盘、键盘、鼠标、显示器、网络等都可以看成是文件。系统中所有的输入输出都通过使用一小组Unix I/O的系统函数调用读写文件来实现的。文件这个概念精简而强大,向应用程序提供了一个统一视图来处理各式各样的I/O设备。

1.8 系统间的网络通信

在前述小节中计算机系统一直被视为一个孤立的硬件和软件的集合,但实际上系统间可以通过网络进行连接,对于每个单一系统而言,网络可以视为一个I/O设备。系统可以将主存中的数据通过网络发送到另一套系统中,也可以通过网络来将其它系统传输的数据读取进主存。

1.9 重要概念

1.9.1 Amdahl定律

Gene Amdahl是计算领域内早期先锋之一,对提升系统某一部分性能所带来的效果进行了简单却有效的归纳,我们称之为Amdahl定律。该定律主要思想是:当我们对系统的一部分进行加速时,其对整个系统的影响取决于该部分的重要程度与加速程度。若系统执行某个程序需要时间 T o l d T_{old} Told,优化部分执行时间占比为 a a a,性能提升比列为 k k k,则优化后程序总的执行时间为

T n e w = ( 1 − a ) T o l d + ( a T o l d ) / k = T o l d [ ( 1 − a ) + a / k ] T_{new}=(1-a)T_{old}+(aT_{old})/k=T_{old}[(1-a)+a/k] Tnew=(1a)Told+(aTold)/k=Told[(1a)+a/k]
系统优化后运行该程序加速比 S = T o l d / T n e w S=T_{old}/T_{new} S=Told/Tnew
S = 1 ( 1 − a ) + a / k S = \frac {1} {(1-a)+a/k} S=(1a)+a/k1
假设系统某个部分初始耗时占比为60% ( a = 0.6 ) (a=0.6) (a=0.6),该部分加速比为3 ( k = 3 ) (k=3) (k=3),则整个系统的加速比为 1 / [ 0.4 + 0.6 / 3 ] = 1.67 1/[0.4+0.6/3]=1.67 1/[0.4+0.6/3]=1.67,虽然我们对系统的一个主要部分做出了重大改进,但是整个系统的加速比明显小于该部分的加速比。该示例说明了Amdahl主要观点——想要显著加速整个系统,必须提升全系统中相当大部分的速度。
Amdahl定律中的一个特殊情况是部分系统加速比 k k k趋于 ∞ ∞ 时的情况:
S ∞ = 1 ( 1 − a ) S_∞= \frac {1} {(1-a)} S=(1a)1
此时整个系统的加速比受限于系统中其余部分的占比。

1.9.2 并发与并行

本书中并发(concurrency)与并行(parallelism)的概念强调的是更加宏观的一种概念,与我们通常理解狭义上的并发与并行存在差异。书中宏观上把一个系统同时处理多个活动叫做并发,而并行则是指利用并发使系统运行得更快。我们狭义中认知的并发与并行与线程息息相关,具体概念如下:
并发(concurrency):本质上是一个CPU同时处理多个线程任务。微观上而言,所有的并发线程都有等候、唤醒、执行等这样的步骤,在微观上它们都是序列被处理的,如果是同一时刻到达的线程也会根据优先级的不同,进行排队等候执行。从宏观上来说,多个同时请求处理的线程看起来像是被同时处理的。
并行(parallelism):本质上是多个线程在多个CPU上运行。并不存在线程间竞争、等待等情况。
按照书中系统层次结构由高到低的顺序介绍三个层次上的并发与并行。

  1. 线程级并发
    在之前进程的抽象概念基础上,可以设计出同时执行多个程序的系统,这就导致了并发。传统意义上,这种并发是由单核CPU系统模拟出的结果,通过在进程间快速切换实现的。这种配置称为单处理器系统。
    当构建一个由单个操作系统内核控制的多个处理器组成的系统时,就可以得到一个多处理器系统。
    多核处理器是将多个CPU集成到一个集成电路芯片上。如图所示,描述了一个十分经典的多核处理器结构,其中微处理器芯片包含4个CPU核,每个核都有自己的L1和L2高速缓存。其中,L1高速缓存可以分为两部分,一部分用于存放最近取到的指令,另一部分用于存放数据。
    多核处理器架构
    超线程亦被称为同时多线程(simultaneous multi-threading),该技术允许一个CPU核同时执行多个线程。该技术涉及到CPU的硬件备份,比如在一个CPU内存在多个程序计数器和寄存器,而只存在一个算术逻辑计算单元。举例来说,Intel i7处理器可以让每个核同时执行两个线程,所以一个4核系统实际上可以并行执行8个线程。
    多核处理器的使用从两个方面提高了系统的性能。首先,其减少了在执行多任务场景下模拟并发的需求;其次,若程序是以多线程方式书写的可以显著提升程序运行速度。
  2. 指令级并行
    在较低抽象层次上,处理器可以同时执行多条指令的能力称为指令级并行。在早期的微处理器中,需要多个时钟周期来执行一条指令,最近的处理器已经达到每个时钟周期2~4条指令的处理速度。指令级并行的一种有效手段是使用流水线(pipelining)。在流水线中,将执行一条指令的活动划分成不同的步骤,将处理器的硬件组织成一系列的阶段,每个阶段执行一个步骤。这些阶段可以并行地操作,用来处理不同指令的不同步骤,能够达到一个时钟周期一条指令的执行速度。当处理器可以达到一个周期一条指令以上的执行速度时,就称之为超标量(super scalar)处理器
  3. 单指令、多数据并行
    在最低层次上,许多处理器都拥有特殊的硬件,允许一条指令产生多个并行操作,这种方式称为单指令、多数据,即SIMD并行。例如,较新的Intel和AMD处理器都具备并行的对8对单精度浮点数做加法的指令。提供这些SIMD指令多是为了提高处理声音、视频等数据的速度
  • 35
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值