深入理解计算机系统 总结索引 | 第1章:计算机系统漫游

计算机系统是 由硬件和系统软件组成的,这些组件是如何工作的 以及这些组件是如何影响程序的正确性和性能的

1、信息就是位+上下文

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

大部分的现代计算机系统 都使用 ASCII 标准来表示文本字符,用一个唯一的单字节大小的整数值 来表示每个字符
在这里插入图片描述
hello.c 程序是以字节序列的方式 储存在文件中的。每个字节都有一个整数值,对应于某些字符。像 hello.c 这样只由 ASCII 字符构成的文件 称为文本文件,所有其他文件都称为二进制文件

系统中的所有信息 —— 包括磁盘文件、内存中的程序、内存中存放的用户数据 以及 网络上传送的数据,都是由一串比特表示的。区分不同数据对象的唯一方法是 读到这些数据对象时的上下文。比如,在不同的上下文中,一个同样的字节序列 可能表示一个整数、浮点数、字符串或者机器指令

需要了解 数字的机器表示方式,因为 与实际的整数和实数是不同的。它们是 对真值的有限近似值,有时候会有意想不到的行为表现

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

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

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

linux> gcc -o hello hello.c

2、GCC编译器驱动程序 读取源程序文件 hello.c,并把它翻译成 一个可执行目标文件 hello。这个翻译过程可分为四个阶段完成。执行这四个阶段的程序(预处理器、编译器、汇编器 和 链接器)一起构成了编译系统
在这里插入图片描述

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

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

  • 优化程序性能。无须为了写出高效代码 而去了解编译器的内部工作。但是,为了在 C 程序中做出好的编码选择,确实需要了解一些机器代码 以及 编译器将不同的 C 语句转化为机器代码的方式。比如,一个 switch 语句是否总是比一系列的 if-else 语句高效得多?一个函数调用的开销有多大?while 循环比 for 循环更有效吗?指针引用比数组索引更有效吗?为什么 将循环求和的结果放到一个本地变量中,会比将其放到一个通过引用传递过来的参数中,运行起来快很多呢? 为什么 只是简单地重新排列一下算术表达式中的括号 就能让函数运行得更快?
  • 理解链接时出现的错误。比如,链接器报告说 它无法解析一个引用,这是什么意思?静态变量和全局变量的区别是什么?如果你在不同的C文件中定义了名字相同的两个全局变量会发生什么?静态库和动态库的区别是什么?我们在命令行上排列库的顺序有什么影响?最严重的是,为什么有些链接错误直到运行时才会出现?
  • 避免安全漏洞。缓冲区溢出错误 是造成大多数网络和Internet服务器上 安全漏洞的主要原因。存在这些错误是因为 很少有程序员能够理解 需要限制从不受信任的源接收数据的数量和格式。学习安全编程的第一步 就是理解数据和控制信息存储在程序栈上的方式会引起的后果

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

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

linux> ./hello
hello, world
linux>

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

4.1 系统的硬件组成

1、总线
贯穿整个系统是 一组电子管道,称作总线,它携带信息字节 并在各个部件间传递。通常总线被设计成 传送定长的字节块,也就是字。字中的字节数 (即字长) 是一个基本的系统参数,每个系统中都不尽相同。现在的大多数机器字长 要么是4个字节(32位),要么是 8个字节(64位)

2、I/O设备
I/O (输入/输出)设备是 系统与外部世界的联系通道。示例系统包括四个I/O 设备:作为用户输入的键盘和鼠标,作为用户输出的显示器,以及用于长期存储数据和程序的 磁盘驱动器(简单地说就是磁盘)。最开始,可执行程序hello 就存放在磁盘上

每个 I/O 设备都通过一个控制器或适配器 与I/O总线相连。控制器和适配器之间的区别主要在于 它们的封装方式

控制器是 I/O 设备本身 或者系统的主印制电路板 (通常称作主板) 上的芯片组(控制器直接与设备硬件交互,处理设备的低级操作,比如 信号传输、数据缓冲、错误检测等)。而适配器 则是一块插在主板插槽上的卡(适配器充当接口或中介,使计算机能够与某些设备进行通信。例如,显卡适配器可以让计算机显示图形内容,网卡适配器可以让计算机连接到网络)。它们的功能 都是在 I/O 总线 和 I/O 设备之间传递信息
在这里插入图片描述
3、主存
主存是 一个临时存储设备,在处理器执行程序时,用来存放程序和程序处理的数据。从物理上来讲,主存是 由一组动态随机存取存储器 (DRAM) 芯片组成的。从逻辑上来讲,存储器 是一个线性的字节数组,每个字节都有其唯一的地址 (数组索引),这些地址是 从零开始的。一般来说,组成程序的每条机器指令 由不同数量的字节构成。与C程序变量相对应的数据项大小 根据类型变化。比如,在运行Linux的x86-64机器上,short 类型的数据需要2个字节,int 和 float 类型需要4个字节,而 long 和 double 类型需要8个字节

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

从系统通电开始,直到系统断电,处理器一直在不断地执行程序计数器指向的指令,再更新程序计数器,使其指向下一条指令
处理器看上去是按照一个非常简单的指令执行模型来操作的,这个模型是由指令集架构决定的。在这个模型中,指令按严格的顺序执行,而执行一条指令 包含一系列步骤。处理器 从程序计数器指向的位置读取指令,解释指令中的位,执行该指令指示的简单操作,然后更新 PC,使其指向下一条指令。而这条指令 并不一定和在内存中刚刚执行的指令 相邻

围绕着主存、寄存器文件 和 算术/逻辑单元(ALU) 进行。寄存器文件 是一个小的存储设备,由一些单个字长的寄存器组成,每个寄存器 都有唯一的名字。ALU 计算新的数据和地址值。下面是一些简单操作的例子,CPU 在指令的要求下可能会执行这些操作:

  • 加载:从主存 复制一个字节或者一个字 到寄存器,以覆盖 寄存器原来的内容
  • 存储:从寄存器复制一个字节或者一个字到主存的某个位置,以覆盖这个位置上的内容
  • 操作:把两个寄存器的内容复制到 ALU,ALU 对这两个字做算术运算,并将结果存放回到一个寄存器中,以覆盖该寄存器中原来的容
  • 跳转:从指令本身中抽取一个字,并将这个字 复制到程序计数器( PC )中,以覆盖 PC 中原来的值

处理器看上去 是它的指令集架构的简单实现,但实际上是 现代处理器使用了非常复杂的机制 来加速程序的执行。因此 将处理器的指令集架构 和 处理器的微体系结构区分开来:指令集架构 描述的是每条机器代码指令的效果;而微体系结构 描述的是处理器实际上是如何实现的

4.2 运行 hello 程序

初始时,shell 程序执行它的指令,等待 输入一个命令。当 在键盘上输入字符串 “./hello” 后,shell 程序 将字符逐—读入寄存器,再把它存放到内存中
在这里插入图片描述
在键盘上敲回车键时,shell 程序就知道 已经结束了命令的输入。然后 shell 执行一系列指令 来加载可执行的 hello 文件,这些指令将 hello 目标文件中的代码和数据 从磁盘复制到主存。数据包括最终会被输出的字符串“hello, world\n”

利用直接存取(DMA)技术,数据可以不通过处理器 而直接从磁盘到达主存
在这里插入图片描述

5、高速缓存至关重要

系统花费了大量的时间 把信息从一个地方挪到另一个地方。hello 程序的机器指令 最初是存放在磁盘上,当程序加载时,它们被复制到主存;当处理器运行程序时,指令又从主存复制到处理器。这些复制就是开销,减慢了程序“真正”的工作。因此,系统设计者的一个主要目标 就是使这些复制操作尽可能快地完成

较大的存储设备 要比较小的存储设备运行得慢,而快速设备的造价 远高于同类的低速设备。加快处理器的运行速度 比加快主存的运行速度 要容易和便宜得多

针对这种处理器与主存之间的差异,系统设计者 采用了更小更快的存储设备,称为高速缓存存储器,作为暂时的集结区域,存放 处理器近期可能会需要的信息
位于处理器芯片上的 L1 高速缓存容量可以达到数万字节,访问速度 几乎和访问寄存器文件一样快。一个容量为数十万到数百万字节的更大的 L2 高速缓存 通过一条特殊的总线连接到处理器。进程访问 L2 高速缓存的时间要比访问 L1 高速缓存的时间长 5 倍,但是这仍然比访问主存的时间快 5~10 倍。L1 和 L2 高速缓存是用一种叫做 静态随机访问存储器 (SRAM) 的硬件技术实现的。比较新的、处理能力更强的系统 甚至有三级高速缓存:L1、L2和L3
系统可以 获得一个很大的存储器,同时 访问速度也很快,原因是利用了高速缓存的局部性原理,即程序具有访问局部区域里的数据和代码的趋势。通过 让高速缓存里存放经常访问的数据,大部分的内存操作 都能在快速的高速缓存中完成
在这里插入图片描述
高速缓存存储器存在的应用:程序员能够利用高速缓存 将程序的性能提高一个数量级

6、存储设备形成层次结构

在处理器 和 一个较大较慢的设备 (例如主存) 之间 插入一个更小更快的存储设备 (例如高速缓存)。每个计算机系统中的存储设备 都被组织成了一个存储器层次结构。在这个层次结构中,从上至下,设备的访问速度 越来越慢、容量越来越大,并且每字节的造价 也越来越便宜
在这里插入图片描述
上一层的存储器 作为低一层存储器的高速缓存。因此,寄存器文件 就是 L1 的高速缓存,L1 是 L2 的高速缓存,L2 是 L3 的高速缓存,L3 是主存的高速缓存,而 主存又是磁盘的高速缓存。在某些具有分布式文件系统的网络系统中,本地磁盘 就是存储在其他系统中磁盘上的数据的高速缓存

7、操作系统管理硬件

当 shell 加载和运行 hello 程序时;以及 hello 输出自己的消息时,shell 和 hello 程序都没有直接访问键盘、显示器、磁盘或者主存。它们依靠操作系统提供的服务。可以把操作系统 看成是 应用程序和硬件之间插入的一层软件。所有应用程序 对硬件的操作尝试都必须通过操作系统
在这里插入图片描述
操作系统有两个基本功能:(1)防止硬件 被失控的应用程序滥用;(2)向应用程序提供简单的机制 来控制复杂而又通常大不相同的底层硬件设备
操作系统 通过几个基本的抽象概念(进程、虚拟内存和文件)来实现这两个功能。文件 是对 I/O 设备的抽象表示,虚拟内存 是对主存和磁盘I/O设备的抽象表示,进程 则是对处理器、主存和 I/O 设备的抽象表示

7.1 进程

程序 看上去是独占地使用处理器、主存和 I/O 设备。处理器 看上去就像在不断地一条接一条地执行程序中的指令,即该程序的代码和数据 是系统内存中唯一的对象。这些假象 是通过进程的概念来实现的

进程 是操作系统 对一个正在运行的程序的一种抽象。在一个系统上 可以同时运行多个进程,而每个进程 看上去都在独占地使用硬件
并发运行,则是指一个进程的指令 和 另一个进程的指令是交措执行的。在大多数系统中,需要运行的进程数 是多于可以运行它们的 CPU 个数的。传统系统 在一个时刻只能执行一个程序,而先进的多核处理器 同时能够执行多个程序。无论是在单核还是多核系统中,一个 CPU 看上去 都是在并发行执行多个进程,这是通过处理器 在进程间切换来实现的。操作系统实现这种交错执行的机制 称为上下文切换。只考虑包含一个 CPU 的单处理器系统的情况

操作系统 保持跟踪进程运行 所需的所有状态信息。这种状态,也就是上下文,包括许多信息,比如 PC 和 寄存器文件的当前值,以及主存的内容。在任何一个时刻,单处理器系统 都只能执行一个进程的代码。当操作系统决定要把控制权 从当前进程转移到某个新进程时,就会进行上下文切换,即保存当前进程的上下文、恢复新进程的上下文,然后 将控制权传递到新进程。新进程 就会从它上次停止的地方开始

示例场景中 有两个并发的进程:shell 进程和 hello 进程。最开始,只有 shell 进程在运行,即 等待命令行上的输入。当 让它运行 hello 程序时,shell 通过调用一个专门的函数,即系统调用,来执行请求,系统调用 会将控制权传递给操作系统。操作系统 保存 shell 进程的上下文,创建一个新的 hello 进程及其上下文,然后 将控制权传给新的 hello 进程。hello 进程终止后,操作系统 恢复 shell 进程的上下文,并将控制权传回给它,shell 还会继续等待下一个命令行输入

从一个进程到另一个进程的转换是 由操作系统内核管理的。内核是操作系统代码 常驻主存的部分。当应用程序需要操作系统的某些操作时,比如 读写文件,它就执行 一条特殊的系统调用指令,将控制权传递给内核。然后内核执行被请求的操作 并返回应用程序。注意,内核不是一个独立的进程。相反,它是系统管理全部进程所用代码 和 数据结构的集合
在这里插入图片描述
实现进程这个抽象概念 需要低级硬件和操作系统软件之间的紧密合作

7.2 线程

一个进程 实际上可以 由多个称为线程的执行单元组成,每个线程 都运行在进程的上下文中,并共享同样的代码和全局数据
多线程之间 比多进程之间 更容易共享数据,也因为线程一般来说都比进程更高效。当有多处理器可用的时候,多线程也是 一种使得程序可以运行得更快的方法

7.3 虚拟内存

为每个进程提供了个假象,即每个进程 都在独占地使用内存。每个进程看到的内存都是一致的,称为 虚拟地址空间
在 Linux 中,地址空间最上面的区域 是保留给 操作系统中的代码和数据的,这对所有进程来说都是一样。地址空间的底部区域 存放用户进程定义的代码和数据。图中的地址是从下往上增大的
在这里插入图片描述
每个进程 看到的虚拟地址空间 由大量准确定义的区构成,每个区都有专门的功能,从最低的地址开始,逐步向上介绍

  • 程序代码和数据。对所有的进程来说,代码是从同一固定地址开始,紧接着的是和 C 全局变量对应的数据位置。代码和数据区是 直接接照可执行目标文件的内容初始化的,在示例中就是可执行文件 hello
  • 堆。代码和数据区后 紧随着的是运行时堆。代码和数据区 在进程一开始运行就被指定大小,与此不同,当调用像 malloc 和 free 这样的 C 标准库函数时,堆可以在运行时 动态地扩展和收缩
  • 共享库。大约在地址空间的中间部分 是一块用来放像 C 标准库和数学库 这样的共享库的代码和数据的区域
  • 栈。位于用户虚拟地址空间顶部的是 用户栈,编译器用它来实现函数调用。和堆一样,用户栈在程序执行期间 可以动态地扩展和收缩。特别地,每次 调用一个函数时,栈就会增长;从一个函数返回时,栈就会收缩
  • 内核虚拟内存。地址空间顶部的区域是 为内核保留的。不允许应用程序 读写这个区域的内容 或者直接调用内核代码定义的函数。相反,必须调用内核来执行这些操作

虚拟内存的运作 需要硬件和操作系统软件之间 精密复杂的交互,包括 对处理器生成的每个地址的硬件翻译。基本思想是 把一个进程虚拟内存的内容 存储在磁盘上,然后 用主存作为磁盘的高速缓存

7.4 文件

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

文件 向应用程序提供了 一个统一的视图,来看待 系统中可能含有的各种各样的 I/O 设备。例如,处理磁盘文件内容的应用程序员 无须了解具体的磁盘技术。进一步说,同一个程序 可以在使用不同磁盘技术的不同系统上运行

8、系统之间利用网络通信

现代系统 经常通过网络 和其他系统连接到一起。从一个单独的系统来看,网络可视作一个 I/O 设备。当系统 从主存复制一串字节到网络适配器时,数据流 经过网络到达另一台机器,而不是 比如说到达本地磁盘驱动器。相似地,系统可以读取 从其他机器发送来的数据,并把数据复制到自己的主存
在这里插入图片描述
随着 Internet 这样的全球网络的出现,从一台主机 复制信息到 另外一台主机 已经成为计算机系统最重要的用途之一。比如,像电子邮件、即时通信、万维网、FTP 和 telnet 这样的应用都是 基于网络复制信息的功能

可以使用 熟悉的 telnet 应用在一个远程主机上运行 hello 程序。假设用本地主机上的 telnet 客户端连接远程主机上的 telnet 服务器。在 登录到远程主机并运行 shell 后,远端的 shell 就在等待接收输人命令。此后在远端运行 hello 程序包括如下五个基本步骤
在这里插入图片描述
在 telnet 客户端键入 “hello” 字符串 并敲下回车键后,客户端软件 就会将这个字符串发送到 telnet 的服务器。telnet 服务器从网络上收到这个字符串后,会把它传送给远端 shell 程序。接下来,远端 shell 运行 hello 程序,并将输出行回给 telnet 服务器。最后,telnet 服务器 通过网络把输出串转发给 telnet 客户端,客户端就将输出串输出到 本地终端上

9、重要主题

系统是 硬件和系统软件 互相交织的集合体

9.1 Amdahl定律

1、对系统的一个部分加速时,其对系统整体性能的影响 取决于 该部分的重要性(即它执行的时间比例)和加速程度(即它的速度提升了多少倍)。若系统执行某个应用程序所需时间为 T_old。假设系统某部分所需时间 与总时间的比例为 α,而该部分性能提升的比例为 k。即该部分初始所需时间为 α T_old,现在所需时间为 (α T_old) / k。因此,总的执行时间应为
在这里插入图片描述
由此,可以计算加速比 S = T_old / T_new 为
在这里插入图片描述
系统的某个部分 初始耗时比例为 60% (α = 0.6),其加速因子为 3 (k = 3)。则 可获得的加速比 1/[0.4 + 0.6/3] = 1.67 倍,要想 显著加速整个系统,必须提升 全系统中相当大的部分的速度

Amdahl 定律一个有趣的特殊情况是 考虑 k 趋向于 ∞ 时的效果。这意味着,可以取系统的某一部分 将其加速到一个点,在这个点上,这部分花费的时间 可以忽略不计
在这里插入图片描述
2、表示相对性能
性能提升最好的表示方法 就是用比例的形式 T_old / T_new,用后缀 “×” 来表示比例,因此,“2.2×” 读作 “2.2倍”
表示相对变化更传统的办法 是用百分比,这种办法 适用于小的变化

9.2 并发和并行

并发 是一个通用的概念,指一个 同时具有多个活动的系统;而术语 并行 是指用并发来使一个系统运行得更快。并行 可以在计算机系统的多个抽象层次上运用。在此,按照系统层次结构中 由高到低的顺序 强调三个层次

1、线程级并发
构建在进程这个抽象之上,能够设计出 同时有多个程序执行的系统,这就导致了 并发。使用线程,甚至能够在一进程中 执行多个控制流
传统意义上,这种并发执行 只是模拟出来的,是通过使一台计算机 在它正在执行的进程间 快速切换来实现的

在以前,即使处理器 必须在多个任务间切换,大多数实际的计算 也都是由一个处理器来完成的。这种配置称为单处理器系统
当构建 一个由单操作系统内核 控制的多处理器组成的系统时,得到了一个多处理器系统
在这里插入图片描述
下图是一个典型多核处理器的组织结构,其中微处理器芯片 有4个CPU核,每个核 都有自己的 L1 和 L2 高速缓存,其中的 L1 高速缓存分为两个部分——一个保存 最近取到的指令,另一个 存放数据。这些核 共享更高层次的高速缓存,以及 到主存的接口
在这里插入图片描述
超线程,有时称为同时多线程,是一项 允许一个CPU执行多个控制流 的技术。它涉及 CPU 某些硬件 有多份备份,比如 程序计数器 和 寄存器文件,而其他的部分只有一份,比如 执行浮点运算的单元。常规的处理器 需要大约 20 000 个时钟周期 来做不同线程间的转换,而超线程的处理器 可以在单个周期的基础上 决定要执行哪一个线程。这使得 CPU 能够更好地利用它的处理资源。比如,假设一个线程 必须等到某些数据被装载到高速缓存中,那 CPU 就可以 继续去执行另一个线程

多处理器的使用 可以 从两方面提高系统性能。首先,它减少了 在执行多个任务时模拟并发的需要。正如前面提到的,即使是 只有一个用户使用的个人计算机 也需要并发地执行多个活动。其次,它可以使得应用程序运行得更快,当然,这必须要求程序 是以多线程方式来书写的,这些线程 可以并行地高效执行

2、指令级并行
在较低的抽象层次,现代处理器可以同时执行 多条指令的属性 称为指令级并行。早期的微处理器,需要多个(通常是 3~10 个)时钟周期 来执行一条指令。最近的处理器 可以保持每个时钟周期 2~4 条指令的执行速率。其实每条指令 从开始到结束可能需要 长得多的时间,大约 20 个或者更多周期,但是处理器 使用了很多聪明技巧来同时 处理多达 100 条指令。如 流水线 (pipelining) 的使用。在流水线中,将执行一条指令所需要的活动 划分成不同的步骤,将处理器的硬件组织成一系列的阶段,每个阶段执行一个步骤。这些阶段 可以并行地操作,用来处理不同指令的不同部分

如果处理器 可以达到 比一个周期一条指令更快的执行速率,就称之为超标量 (super-scalar) 处理器

3、单指令、多数据并行
在最低层次上,许多现代处理器 拥有特殊的硬件,允许一条指令 产生多个可以并行执行的操作,这种方式称作单指令、多数据,即 SIMD 并行

提供这些 SIMD 指令多是 为了提高处理影像、声音和视频数据应用的执行速度。虽然有些编译器 会试图从 C 程序中自动抽取 SIMD 并行性,但是更可靠的方法是 用编译器支持的特殊的向量数据类型 来写程序,比如 GCC 就支持向量数据类型

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

抽象的使用 是计算机科学中最为重要的概念之一。例如,为一组函数规定一个简单的应用程序接口 (API) 就是一个很好的编程习惯,程序员 无须了解它内部的工作 便可以使用这些代码

在处理器里,指令集架构 提供了 对实际处理器硬件的抽象。使用这个抽象,机器代码程序 表现得就像运行在一个一次只执行一条指令的处理器上。底层的硬件远比抽象描述的要复杂精细,它并行地执行多条指令,但又总是 与那个简单有序的模型保持一致。只要执行模型一样,不同的处理器实现 也能执行同样的机器代码,而又提供不同的开销和性能
在这里插入图片描述
三个抽象:文件是 对 I/O 设备的抽象,虚拟内存是 对程序存储器的抽象,而进程是 对一个正在运行的程序的抽象。再增加一个新的抽象:虚报机,它提供 对整个计算机的抽象,包括 操作系统、处理器和程序

10、小结

计算机内部的信息 被表示为一组组的位,它们依据 上下文有不同的解释方式。程序被其他程序翻译成不同的形式,开始时 是 ASCII 文本,然后被 编译器 和 链接器 翻译成二进制可执行文件

处理器读取 并解释 存放在主存里的二进制指令。因为 计算机花费了大量的时间在 内存、I/O设备 和 CPU寄存器之间 复制数据,所以 将系统中的存储设备分成层次结构——CPU寄存器在顶部,接着是多层的硬件高速缓存存储器、DRAM主存 和 磁盘存储器。在层次模型中,位于更高层的存储设备 比低层的存储设备要更快,单位比特造价也更高。层次结构中 较高层次的存储设备可以作为较低层次设备的高速缓存

操作系统内核是应用程序和硬件之间的媒介。它提供三个基本的抽象:1) 文件是对 I/O 设备的抽象;2) 虚拟内存是对 主存和磁盘的抽象;3) 进程是 处理器、主存和 I/O 设备的抽象
最后,网络提供了 计算机系统之间通信的手段。从特殊系统的角度来看,网络就是一种 I/O 设备

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值