穿整个系统的是一组电子管道,称作总线
!!! info 字通常描述一个字节数,也可称为 字长,它是一个基本的系统参数,在各个系统中都不尽相同,现在的大多数机器字长要么是4个字节(32bit), 要么是8个字节(64bit)
I/O设备 是系统与外部世界的联系通道,它们都通过一个控制器或适配器与 I/O总线(
!!! info 控制器和适配器之间的区别主要在于它们的封装方式
控制器是 I/O设备 本身或者系统的主印制电路板(通常称作主板)上的芯片组
适配器则是一块插在主板插槽上的卡
主存是一个临时存储设备,在处理器执行程序时,用来存放程序和程序处理的数据
从物理上来说,主存是由一组动态随机存取存储器(
中央处理单元(
系统通电开始,直到系统断电,处理器一直在不断地执行程序计数器指向的指令,再更新程序计数器,使其指向下一条指令;这使得处理器看上去是按照一个非常简单的指令执行模型(由指令集架构决定)来操作的,在这个模型中,指令按照严格的顺序执行
处理器在执行指令时,处理器会从 PC 指向的内存处读取指令并解释指令中的位(
!!! info PC 是计算机科学中使用的一个术语,他是一个抽象的概念,在 x86 兼容 CPU 上,CS段寄存器 和 IP寄存器 是实现计算机科学中的 PC 这个概念的具体设施
根据机械原理,较大的存储设备要比较小的存储设备运行得慢,而快速设备的造价远高于同类的低速设备处理器。 比如说,在访问寄存器与主存之间,二者的时间差异高达100倍,但是,仅依赖于典型寄存器的几百字节的容量根本无法处理系统上的巨量信息,然而盲目扩张寄存器的大小反而会导致设备造价的急速升高
针对于这种相互矛盾且尴尬的差异,系统设计者将一种称为高速缓存存储器(
高速缓存的功能,我们可以总结为: 它作为暂时的集结区域,存放处理器近期可能会需要的信息。
在现代处理器当中,通常都附带了编号为
使得高速缓存能够拥有近似寄存器访问速度的原因,除了因为其是通过一种叫做静态随机访问存储器 (
在处理器和一个较大较慢的设备(例如主存)之间插入一个更小更快的存储设备(例如高速缓存)的想法已经成为一个普遍的观念
实际上,每个计算机系统中的存储设备都被组织成了一个存储器层次结构。其主要思想是上一层的存储器作为低一层存储器的高速缓存,例如:寄存器文件就是
如下图所示(寄存器文件记为
仅依靠一个程序自身的能力是无法访问系统中的其他硬件设备的,取而代之的,它需要依靠于操作系统所提供的一些服务去进行。我们可以把操作系统看成是应用程序和硬件之间插入的一层软件,所有应用程序对硬件的操作尝试都必须通过操作系统来完成
操作系统通过几个基本的抽象概念(进程、虚拟内存和文件)来实现两个基本功能: (1) 防止硬件被失控的应用程序滥用;(2) 向应用程序提供简单一致的机制来控制复杂而又通常大不相同的低级硬件设备。如下图所示,文件是对I/O设备的抽象表示;虚拟内存是对主存和磁盘I/O设备的抽象表示,进程则是对处理器、主存和I/O 设备的抽象表示。
程序在现代系统上运行时,操作系统会提供一种假象,就好像系统上只有这个程序在运行并独占地使用处理器、主存和I/O设备;而处理器看上去就像在不间断地一条接一条地执行着这个独占程序中的代码指令,即该程序的代码和数据是系统内存中唯一的对象。这些假象则是通过进程的概念来实现的
进程是操作系统对一个正在运行的程序的一种抽象,它是计算机科学中最重要和最成功的概念之一
现代处理器(单核)虽然唯有且仅有一个 PC 寄存器,但是它依然能够在某一时刻下同时运行着多个进程。操作系统通过一种暂且称之为进程间切换的机制,以至于让多个进程之间的指令可以被处理器交错执行,因为处理器的执行速度过快,它能够在人肉眼无法察觉的时间范围内将 PC 寄存器从此刻正在执行的进程切换到另一个进程身上,以此来完成一种 并发执行 的假象。对于这种机制,我们会将它称之为 上下文切换(
上下文
!!! INFO 从一个进程到另一个进程的转换是由操作系统内核(kernel)管理的
内核是操作系统代码常驻主存的部分。当应用程序需要操作系统的某些操作时,比如读写文件,它就执行一条特殊的系统调用(system call)指令,将控制权传递给内核。然后内核执行被请求的操作并返回应用程序
注意,<u>**内核不是一个独立的进程**</u>,相反,它是系统管理全部进程所用代码和数据结构的集合
尽管通常我们认为一个进程只有单一的控制流,但是在现代系统中,一个进程实际上可以由多个称为线程的执行单元组成,每个线程都运行在进程的上下文中,并共享同样的代码和全局数据
由于网络服务器中对并行处理的需求,线程成为越来越重要的编程模型,因为多线程之间比多进程之间更容易共享数据,也因为线程一般来说都比进程更高效,当有多处理器可用的时候,多线程也是一种使得程序可以运行得更
虚拟内存是一个抽象概念,它为每个进程提供了一个假象,即每个进程都在独占地使用主存。每个进程看到的内存都是一致的,称为虚拟地址空间
下图展示了一张 linux 虚拟地址空间内存模型图,在地址空间最上面的区域是保留给操作系统中的代码和数据的,这对所有进程来说都是一样,地址空间的底部区域存放用户进程定义的代码和数据
每个进程看到的虚拟地址空间由大量准确定义的区构成,每个区都有专门的功能
-
Program code and data: 对所有的进程来说,代码是从同意固定地址开始,紧接着的是和C全局变量相对应的数据位置
-
Heap: 代码和数据区后紧随着的是运行时堆,代码和数据区在进程一开始运行时就被指定了大小,与此不同,当调用像
$malloc$ 和$free$ 这样的C标准库函数时,堆可以在运行时动态地扩展和收缩 -
Shared libraries: 大约在地址空间的中间部分是一块用来存放像C标准库和数学库这样的共享库的代码和数据的区域
-
Stack: 位于用户虚拟地址空间顶部的是用户栈,编译器用它来实现函数调用。和堆一样,用户栈在程序执行期间可以动态地扩展和收缩,例如当每次我们调用一个函数时栈就会增长;从一个函数返回时栈就会收缩
-
Kernel virtual memory: 地址空间顶部的区域是为内核保留的,不允许应用程序读写这个区域的内容或者直接调用内核代码定义的函数,相反,它们必须调用内核来执行这些操作
文件就是字节序列仅此而已。每个I/O设备,包括磁盘、键盘、显示器甚至网 络,都可以看成是文件。系统中的所有输入输出都是通过使用一小组称为 Unix I/O 的系统函数调用读写文件来实现的
文件这个简单而精致的概念是非常强大的,因为它向应用程序提供了一个统一的视图,来看待系统中可能含有的所有各式各样的I/O设备。例如,处理磁盘文件内容的应用程序员可以非常幸福,因为他们无须了解具体的磁盘技术,进一步说,同一个程序可以在使用不同磁盘技术的不同系统上运行(同时还解决了平台差异化的问题,提升了程序的便携性)
若系统执行某应用程序需要时间为
由此,可以计算加速比
$$
S= \frac {1}{(1- {\small \alpha} \, + \, {\small \alpha} \, / \, {\small \kappa})}
$$
数字计算机的整个历史中,有两个需求是驱动进步的持续动力∶ 一个是我们想要计算机做得更多,另一个是我们想要计算机运行得更快
当处理器能够同时做更多的事情时,这两个因素都会改进。我们用的术语 并发(
构建在进程这个抽象之上,我们能够设计出同时有多个程序执行的系统,这就导致了并发。使用线程,我们甚至能够在一个进程中执行多个控制流
对于处理器的配置不同,我们通常会将处理器的类型分为 单处理器系统 和 多处理器系统
在以前,大多数实际的计算都是由一个处理器来完成的,我们将这种系统架构称之为 单处理器系统。即便只有一个核心在工作,单处理器系统依然提供了多个用户同时与系统交互的能力,虽然实际上只是通过
当构建一个由单操作系统内核控制的多处理多处理器器组成的系统时,我们便可以得到一个多处理器系统。随着现代计算机的发展,还有 多核处理器 与 超线程技术 (
多核处理器是将多个
!!! info
超线程,有时称为同时多线程 ( $simultaneous\ multi-threading$ ),是一项允许一个 CPU 执行多个控制流的技术。它涉及 CPU某些硬件有多个备份,比如程序计数器和寄存器文件,而其他的硬件部分只有一份,比如执行浮点算术运算的单元。常规的处理器需要大约 20000 个时钟周期做不同线程间的转换,而超线程的处理器可以在单个周期的基础上决定要执行哪一个线程。这使得 CPU 能够更好地利用它的处理资源
我们可以总结多线程技术拥有这几个优势: (1) 对于硬件部分存在多种备份方式 (2) 更高效的上下文切换的时机选择
在较低的抽象层次上,现代处理器可以同时执行多条指令的属性称为 指令级并行
早期的微处理器,如 1978 年的 Intel 8086,需要多个 (通常是 3 ~ 10 个) 时钟周期来执行一条指令。最近的处理器通过一种名为 流水线 (
!!! info
如果处理器可以达到比一个周期一条指令更快的执行速率,就称之为超标量 ( $super\ scalar$) 处理器
在最低层次上,许多现代处理器拥有特殊的硬件,允许一条指令产生多个可以并行执 行的操作,这种方式称为 单指令、多数据,即 SIMD 并行
!!! info
较新几代的 Intel 和 AMD 处理器都具有并行地对 8 对单精度浮点数 ( C数据类型 float ) 做加法的指令,供这些
对于跨越多字节的程序对象,我们必须建立两个规则,(1)这个对象的地址是什么; (2)这个对象在内存中如何排列这个对象所表示的字节数据。在几乎所有的机器上,多字节对象都被存储为连续的字节序列,对象的地址为所使用字节中最小的地址
考虑一个
在大多数时候,字节顺序对于开发者来说是完全不可见的,无论为哪种类型的机器所编译的程序都会得到同样的结果。 但是在某些时候需要我们引起一些注意,比如当我们编写网络程序的时候出现两端机器都采用不同字节序的情况,还有就是在阅读表示 整数数据 的字节序列的时候,例如下面摘自某个通过小端序机器所生成程序的机器级代码的文本表示
" 0x200b43 实际存储为 [43 0b 20 00],即最低位在左边,最高位在右边
4004d3: 01 05 43 0b 20 00 add %eax,0x200b43(%rip)
我们要重点理解字节顺序的含义,它是以我们写入的值作为字节顺序存储的依据,而不是实际使用的类型,比方说我们有一个字符串 "123456",它在机器级表示为 31(1) 32(2) 33(3) 34(4) 35(5) 36(6),而不是与上面例子所相反的结果。从这其实也可以看出,文本数据比二进制数据具有更强的平台独立性
二进制值是计算机编码、 存储和操作信息的核心,所以围绕数值
布尔运算
布尔运算
布尔运算
布尔运算
我们可以将上述 4 个布尔运算扩展到位向量的运算,位向量就是固定长度为
假设
| 希伯来文(Hebrew) | 希腊文(Greek) | 英语(English) |
|---|---|---|
| 弥赛亚(Messiah) | 基督(Christ) | 受膏者(the anointed one) |












