banner
zam

zam

君子可内敛而不可懦弱,面不公可起而论之。
twitter
telegram
github

第一章 计算机系统漫游

问题#

  1. 信息的两个要素。
    位 + 上下文。
  2. 文本文件是有什么组成的?
    由 ASCII 标准表是的字符。
  3. 从源程序到目标程序的四个阶段?
    预处理,编译,汇编,链接。
  4. 系统硬件组成的四个部分?
    处理器,主存,I/O 设备,总线。
  5. 从逻辑上看主存是什么样的?
    一串线性字节数组,每个字节有唯一的地址。
  6. 主存硬件上是什么组成的?
    动态随机存储内存(DRAM)。
  7. 处理器的核心是什么?它主要干什么?
    程序计数器,存储下一条指令的地址。
  8. 用最简单的方式描述处理器运行的过程?
    执行 PC 所指向的指令,然后更新 PC。
  9. 指令操作围绕哪三个部件执行?
    主存,寄存器和 ALU(运算器)。寄存器存放指令、数据、状态等,ALU 进行运算。
  10. 四个指令操作:加载、存储、操作、跳转分别是什么?
    (1) 加载(Load):这是从内存中将数据加载到寄存器或其他处理器内部的操作。
    (2) 存储(Store):这是将数据从寄存器或其他处理器内部存储到内存中的操作。
    (3)操作(Arithmetic/Logical):这涉及到在处理器内部执行算术或逻辑运算的操作。算术操作包括加法、减法、乘法、除法等;逻辑操作涉及位操作,如与、或、非、异或等。
    (4)跳转(Jump):这是用于改变程序执行顺序的操作。
  11. 指令集架构和微体系架构分别是什么?
    描述指令的效果。硬件的实现方式。
  12. CPU 执行一个目标文件的流程?
    把文件存磁盘读取到寄存器,再放到主存(直接存取可以实现不经过寄存器),执行目标文件的指令。
  13. 从源程序到得出运行结果的整个流程?
    源程序 -> 目标程序 -> 处理器执行目标程序
  14. 主存比磁盘快多少?寄存器比磁盘快多少?
    千万倍,百倍。
  15. L1 cache 位于哪里?有多大?有多快?L2 呢?
    CPU 上,几十 MB,比 L2 快 5 倍;L2 通过特殊的总线与 CPU 相连,几百 MB~ 几 GB,比主存快 5~10 倍。
  16. 高速缓存是怎么用的?
    存储需要经常访问的数据。
  17. 存储器层次结构有几层,主要思想是什么?
    7 层,用上一层做下一层的高速缓存。
  18. 操作系统的三个基本抽象概念是什么?
    文件、虚拟内存、进程。
  19. 什么是进程?进程有什么特点?
    系统正在执行的程序,系统在某一时刻只能执行一个进程。
  20. 什么是操作系统内核?主要做什么?
    操作系统常驻主存的部分,负责管理进程的切换,方式是上下文切换。
  21. 什么是虚拟地址空间?由几个部分组成?
    进程看到的是一个虚拟的地址空间。从低到高依次是程序代码和数据、运行时堆、共享库、用户栈、内核虚拟内存。
  22. 并发和并行的区别,和顺序执行的区别?
    并发是交错执行,并行是同时进行,顺序是执行完一个再执行另一个。
  23. 什么是线程级并行?
    四个核一个核一个线程就是四线程并行。
  24. 什么是超线程?
    超线程可以实现一个核两个线程。因为如 PC、寄存器等硬件有多个备份,可以在一个线程需要等待拷贝的时候执行另一个线程。相当于充分利用硬件交错执行不同线程。
  25. 什么是指令级并行?
    一个指令需要 20 多个时钟周期,并行后一秒可以执行 2~4 个指令。
  26. 什么是单指令、多数据并行?
    一个指令的几个简单操作并行执行(硬件层面)。
  27. 什么是指令集架构?
    一种抽象,看起来好像是指令顺序依次执行,实际上背地里是指令级并行。

1.1 信息就是位 + 上下文#

程序#

  • 程序的生命周期从源程序(源文件) 开始。源程序实际上就是由 0 和 1 组成的位序列
  • 一般由ASCII 标准来表示文本字符,实际上使用一个字节的整数值来表示一种字符。
  • 源文件中每个文本行都是以看不见的 '\n' 结束的。
  • 只由 ASCII 字符组成的文件称为文本文件,其他都是二进制文件。如.cpp 文件就是文本文件。
  • 系统中的所有信息都是有一串比特(bit:位)表示的,区分不同数据对象的唯一方法就是根据上下文

C 语言的特点#

  1. C 语言小而简单
  2. C 语言是为了实现 unix 而设计的
  3. C 语言与 unix 关系密切

C 语言是系统级编程的首选,也非常适用于应用级程序。

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

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

从源程序到目标程序的四个步骤:#

  1. 源程序被预处理器处理得到修改后的源程序(文本文件,hello.i)
  2. 再由编译器处理得到汇编程序(文本文件,hello.s)
  3. 汇编程序由汇编器处理得到可重定位目标程序(二进制文件,hello.o)
  4. 最后由链接器链接得到可执行目标程序(二进制文件,hello)
  • 预处理阶段:预处理器(cpp)根据以字符 #开头的命令,修改原始的 C 程序。比如hello.c中第一行的#include <stdio.h>命令告诉预处理器读取洗头文件stdio.h的内容,并把它直接插入程序文本中。最终得到另一个 C 程序,通常是以 .i 作为文件扩展名。
  • 编译阶段:编译器(ccl)将文本文件hello.i翻译成文本文件hello.s,它包含一个汇编语言程序。该程序包含函数main的定义,如下所示:
    1   main:
    2     subq  $8,  %rsp
    3     movl  $.LC0,  %edi
    4     call  puts
    5     movl  $0,  %eax
    6     addq  $8,  %rsp
    7     ret
  • 汇编阶段:接下来,汇编器(as)将hello.s翻译成机器语言指令,将这些指令打包成一种叫做可重定位目标程序(relocatable object program)的格式,并将结果保存在目标文件hello.o中,hello.o文件是一个二进制文件,它包含的 17 个字节是函数main的指令编码。
  • 链接阶段:应该注意的是,hello程序调用了printf函数,它是每个 C 编译器都提供的标准 C 库中的一个函数。printf函数存在于一个名为printf.o的单独预编译好了的目标文件中。链接器(ld)负责将该文件合并到我们的hello.o文件中,最终得到一个可执行目标文件hello文件,可以被加载到内存中,由系统执行。

1.3 了解编译系统如工作是大有用处的#

用处#

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

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

shell 是一个命令行解释器, 它输出一个提示符(>>), 等待输入一个命令行,然后执行命令。如果输入的是可执行文件的名字,就执行该文件。

1.4.1 系统的硬件组成#

主要包括总线、I/O 设备、处理器、主存储器四个部分。

总线#
  • 总线一次可以传输一个定长的字节快,称为字。64 位系统即总线一次可以传输 64 位(8 字节),这里一个字就是 8 字节。
I/O 设备#
  • 每个 I/O 设备通过一个控制器适配器与 I/O 总线相连。
  • 控制器是 I/O 设备本身或主板上的芯片组,适配器则是一块插在主板上的卡。
主存#
  • 主存是由一组动态随机存取内存(DRAM) 组成的。
  • 从逻辑上看,存储器是一个线性的字节数组,每个字节都有唯一的地址
处理器#
  • 处理器是解释存储在主存中指令的引擎。
  • 处理器的核心是一个程序计数器(PC)
  • 程序计数器是一个大小为一个字的存储设备,存储 CPU 即将执行的下一条指令的地址.
  • 处理器就是在不断执行程序计数器指向的指令。每执行一条,程序计数器就更新一下,指向下一条指令
  • 处理器会按照指令执行模型(指令集架构) 解释指令中的位并执行相应操作
  • 每条指令的操作是围绕主存、寄存器文件、算术逻辑单元(ALU) 进行的。
寄存器文件#
  • 单个字长,有唯一的名字。
ALU#
  • 简单指令的操作:
    • 加载:从主存复制一个字或字节到寄存器,覆盖原来内容。
    • 存储:从寄存器复制一个字或字节到主存,覆盖原来内容。
    • 操作:把两个寄存器的内容复制到 ALU,ALU 对这两个字做算术运算,并把结果存到一个寄存器中。
    • 跳转:从指令中抽取一个字复制到程序计数器中,覆盖原来内容。

区分处理器指令集架构和微体系架构

  • 指令集架构:每条机器指令的效果。
  • 微体系架构:处理器实际上是如何实现的。

1.4.2 运行 hello 程序#

  • 执行目标文件时,shell 程序将位于磁盘目标文件中的字符逐个读入寄存器,然后放到主存中。之后处理器就开始执行目标文件的机器语言指令,从mian程序开始。利用直接存储器存取(DMA) 可以不通过寄存器,直接将数据从磁盘读到内存。
  • 整个流程读取文件字符到寄存器 -> 存储到主存 -> 执行指令 -> 加载hello wolrd到寄存器 -> 复制到显示器 -> 显示

1.5 高速缓存至关重要#

  • 从主存读取一个字比磁盘快 1000 万倍
  • 从寄存器文件读取比主存快 100 倍
  • 高速缓存(cache)用来解决处理器和主存间的差异
  • L1 高速缓存位于 CPU 上,容量为数万字节(几十 MB)。L1 比 L2 快 5 倍。
  • L2 高速缓存通过一条特殊的总线与 CPU 连接,容量为数十万到数百万字节(几百 MB 到几 GB)。L2 比主存快5~10倍。
  • 通过让高速缓存里存放可能经常访问的数据,让大部分的内存操作都在高速缓存中完成。

1.6 存储设备形成层析结构#

  • 存储器结构共 7 层,主要思想上一层的存储器作为低一层的高速缓存。
  • 从上到下,容量更大,运行更慢,每字节价格更便宜。
    • 0 层:寄存器
    • 1 层:L1 高速缓存(SRAM)
    • 2 层:L2 高速缓存 (SRAM)
    • 3 层:L3 高速缓存(SRAM)
    • 4 层:主存(DRAM)
    • 5 层:本地二级存储(本地磁盘)
    • 6 层:远程二级存储(分布式文件系统,Web 服务器)

1.7 操作系统管理硬件#

  • 操作系统的两个基本功能
    1. 防止硬件被失控的应用程序滥用。
    2. 向应用程序提供简单一致的机制来控制复杂的低级硬件设备
  • 操作系统所应用的三个基本的抽象概念
    1. 进程:对处理器、主存和 I/O 设备的抽象表示。
    2. 虚拟内存:对内存和磁盘的抽象表示。
    3. 文件:对 I/O 设备的抽象表示。

1.7.1 进程#

  • 进程:对操作系统正在运行的程序的一种抽象。
  • 并发运行:一个进程的指令和另一个进程的指令是交错执行的。一个系统可以同时运行多个进程,实际上这些进程是并发运行的。
  • 操作系统通过上下文切换来实现并发运行。上下文是跟踪进程运行所需的所有状态信息,可能存在于 PC、寄存亲文件、主存等地方。
  • 任何时刻,单处理只能执行一个进程的代码
  • 操作系统内核操作系统代码常驻内存的部分,从一个进程到另一个进程的转换是由内核管理的
  • 内核不是一个独立的进程,是一系列代码和数据结构的集合。
  • 当应用程序需要操作系统的某些操作时,就把控制权传递给内核,内核完成操作后返回应用程序。

1.7.2 线程#

  • 一个进程是由多个线程组成,每个线程都运行在进程的上下文中,共享同样的代码和全局数据
  • 多线程之间比多进程之间更容易共享数据,且线程一般来说比进程更高效。

1.7.3 虚拟内存#

  • 机器程序将内存视为一个庞大的字节数组,称为虚拟内存
  • 内存的每个字节由地址来标识,所有可能地址的集合就是虚拟地址空间
  • 虚拟内存使每个进程都以为自己独占了主存。每个进程看到的内存都是一致的,即虚拟地址空间
  • 在 Linux 中,每个进程看到的虚拟地址空间由以下几个部分组成:
    1. 程序代码和数据
    2. 堆(运行时堆)
    3. 共享库
    4. 栈(用户栈)
    5. 内核虚拟内存
  • 地址从低到高,最高层的内核虚拟内存保存的是操作系统中的代码和数据,这部分每个进程都一样
  • 程序代码和数据:对所有进程来说,代码都是从同一个固定地址开始,紧接着是与全局变量对应的数据区。代码和数据区都是按照可执行文件的内容初始化的。代码和数据区在进程开始运行时就被指定了大小
  • :运行时堆是根据mallocfree函数的调用在运行时动态地扩展和收缩的
  • 共享库:地址空间的中间部分用来存放共享库的代码和数据。如 C 标准库、数学库等都属于共享库
  • :用户栈和堆一样,在程序执行期间可以动态地扩展和收缩,编译器用它来实现函数调用。当调用函数时,栈增长,从函数返回时,栈收缩

1.7.4 文件#

  • 文件就是字节序列,仅此而已。
  • 每个 I/O 设备,包括磁盘、键盘、显示器、网络,都可以看成是网络

1.8 系统之间利用网络通信#

  • 从一个单独的系统而言,网络可以视为一个 I/O 设备
    + 以在一个远端服务器运行程序为例,在本地输入,在远端执行,执行结果发送回本地输出。

1.9 主要主题#

1.9.1 Amdahl 定律#

  • Amdahl 定律的主要观点:要加速整个系统,必须是提升全系统中相对大的部分。

1.9.2 并发和并行#

  • 区分并发与并行:
    • 并发:一个通用的概念,指一个同时具有多个活动的系统
    • 并行:用并发来使系统运行得更快
  • 并行可以在多个抽象层次上运用。从高到低有以下三个层次:
  1. 线程级并行
    • 传统意义上的并发执行是通过单处理器在进程间快速切换模拟出来的。
    • 多处理器系统由一个操作系统控制多个 CPU。结构如下

高速缓存

  • L1 高速缓存被分为两个部分: 一个保存最近取到的指令,一个存放数据。
  • 超线程又称同时多线程,它允许一个 CPU 执行多个控制流。 CPU 有的硬件有多个备份,比如程序计数器和寄存器文件,而其他硬件只有一份,比如浮点算术运算单元。常规 CPU 需要约 ** 20000** 个时钟周期来切换线程,超线程 CPU 可以单个周期的基础上切换线程,比如一个线程在等待数据装在到高速缓存,CPU 就可以去执行另一个线程。
  • i7 处理器每个核执行两个线程,所以是** 4 核 8 线程 **,8 个线程都并行执行。
  1. 指令级并行
    • 每条指令从开始到结束一般需要 20 个或更多的时钟周期,通过指令级并行,可以实现每个周期 2~4 条指令的执行速率。
    • 如果比一个周期一条指令更快,就称为超标量处理器,现在一般都是超标量。
  2. 单指令、多数据并行
    • 在最低层次上,现代处理器允许一条指令产生多个可以并行执行的操作,称为单指令、多数据并行,即 SIMD 并行。

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

操作系统的抽象

  • 指令集架构是对 CPU 硬件的抽象,CPU看起来像一次只执行机器代码程序的一条指令,实际上底层硬件并行地执行多条指令
  • 虚拟机是对整个计算机系统的抽象,包括操作系统、处理器和程序。
加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。