深入理解计算机系统

第1章 计算机系统漫游

信息就是位+上下文

系统中的所有信息——包括磁盘文件,存储器中的程序,存储器中存放的用户数据以及网络上传送的数据,都是由一串位表示的。区分不同数据对象的唯一方法是我们读到这些数据对象时的上下文。

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

预处理器,编译器,汇编器和链接器一起构成了编译系统(compilation system)。

  1. 预处理阶段。预处理器(cpp)根据以字符#开头的命令, 修改原始C程序, 通常以.i作为文件扩展名。
  2. 编译阶段。 编译器(ccl)将.i翻译成.s, 它包含一个汇编语言程序。
  3. 汇编阶段。 汇编器(as)将.s翻译成机器语言指令, 把这些指令打包成可重定位目标程序(relocatable object program)的格式, 保存在.o文件中。
  4. 链接阶段。 链接器(ld)合并预编译好的文件, 生成可执行目标文件。

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

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

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

  1. 系统的硬件组成:
    • 总线
    • I/O设备
    • 主存
    • 处理器
  2. CPU在指令的要求下可能会执行以下操作:
    • 加载:把一个字节或者一个字从主存复制到寄存器, 以覆盖寄存器原来的内容。
    • 存储:把一个字节或者一个字从寄存器复制到主存的某个位置, 以覆盖这个位置上原来的内容。
    • 操作:把两个寄存器的内容复制到ALU, ALU对这两个字做算术操作, 并将结果存放在一个寄存器中, 以覆盖该寄存器中原来的内容。
    • 跳转:从指令本身抽取一个字, 并将这个字复制到程序计数器(PC)中, 以覆盖PC原来的值。

高速缓存

针对处理器与主存之间的差异, 系统设计者采用了更小, 更快的存储设备, 即告诉缓存存储器(简称高速缓存), 作为暂时的集结区域。L1和L2高速缓存是用一种叫做静态随机访问存储器(SRAM)的硬件技术实现的。

存储设备形成层次结构

存储器层次结构的主要思想是一层上的存储器作为低一层存储器的高速缓存。

操作系统管理硬件

所有应用程序对硬件的操作尝试都必须通过操作系统

操作系统有两个基本功能:

  1. 防止硬件被失控的应用程序滥用
  2. 向应用程序提供简单一致的机制来控制复杂而又大相径庭的低级硬件设备

操作系统通过几个基本的抽象概念来实现这两个功能:

  1. 进程
    • 进程是操作系统对一个正在运行的程序的一种抽象
    • 处理器在进程间切换来实现并发执行多个进程, 这种机制成为上下文切换
    • 操作系统保持跟踪进程运行所需的所有状态信息, 也就是上下文, 当处理器决定要把控制权从当前进程转移到一个新进程时,就会进行上下文切换
  2. 线程
    • 一个进程可以由多个成为线程的执行单元组成, 每个线程都运行在进程的上下文中,并共享同样的代码和全局数据
  3. 虚拟存储器 虚拟存储器为每个进程提供了抽象概念, 即虚拟地址空间
    • 程序代码和数据 对于所有的进程来说, 代码是从同一固定位置开始的, 紧接着的是和C全局变量相对应的数据位置
    • 堆 紧接着是运行时堆, 可以在运行时动态的扩展和收缩
    • 共享库 大约在地址空间的中间部分是一块用来存放像C标准库和数据的区域
    • 栈 位于用户虚拟地址顶部的是用户栈, 编译器用它来实现函数调用
    • 内核虚拟存储器 地址空间顶部是的区域是为内核保留的, 内核总是驻留在内存中, 是操作系统的一部分
  4. 文件
    • 文件就是字节序列,仅此而已

系统之间利用网络通信

  1. 网络可视为一个I/O设备

重要主题

  1. 并发和并行 并发(concurrency)是一个通用概念, 指一个同时具有多个活动的系统;并行(parallelism)指的是用并发使一个系统运行得更快
    • 线程级并发 超线程, 有时称为同时多线程(simultaneous multi-threading), 是一项允许一个CPU执行多个控制流的技术
    • 指令级并行 在较低的抽象层次上, 处理器可以同时执行多条指令的属性成为指令级并发
    • 单指令,多数据并行 即SIMD并行 一条指令产生多个可以并行执行的操作
  2. 计算机系统中抽象的重要性
    • 抽象的使用是计算机科学中最重要的概念之一

第2章 信息的表示和处理

现代计算机存储和处理的信息以二值信号表示。

信息存储

大多数计算机使用8位的块, 或者字节(byte), 作为做小的可寻址的存储器单位。机器级程序将存储器视为一个非常大的字节数组,称为虚拟存储器(virtual memory)。存储器的每个字节都由一个唯一的数字标识, 称它为地址(address),所有可能地址的集合称为虚拟地址空间(virtual address space)

  1. 十六进制表示法
    • 用十六进制(hex)书写, 一个字节的值域为00-FF
    • 二进制与十六进制的转换比较直接, 可以一次执行一个十六进制数字的转换(二进制每4位组, 对应一个十六进制数值)
    • 在C语言中, 以0x或0X开头的数字常量被认为是十六进制的值
    • 每台计算机都有一个字长(word size), 指明整数和指针数据的标称大小(nominal size)。因为虚拟地址是以这样的一个字来编码的, 所以字长决定的最重要的系统参数就是虚拟地址空间的最大大小。
  2. 数据大小
    • 计算机和编译器都支持多种不同方式编码的数字格式, 如2字节,4字节,8字节整数和4字节和8字节浮点数。
  3. 寻址和字节顺序
    • 对于跨越多字节的程序对象,必须建立两个规则:这个对象的地址是什么, 以及在存储器中如何排列这些字节
    • 几乎所有的机器上, 多字节对象都被存储为连续的字节序列, 对象的地址为所使用字节中最小的地址。
    • 最低有效字节在最前面的方式称为小端法(little endian);最高有效字节在最前面的方式称为大端法(big endian)。对于大多数应用程序员来说, 他们机器所使用的字节顺序是完全不可见的。不过有些时候,字节顺序会成为问题:
    1. 网络应用程序的代码编写必须遵守建立的关于字节顺序的规定, 以确保发送方机器将它的内部表示转换成网络标准, 而接受方机器则将网络标准转换为它的内部表示。

    2. 当阅读表示整数数据的字节序列时字节顺序也很重要。通常在检查机器级程序时会出现这种情况。如:反汇编(disassembler)。

    3. 当编写规避正常的类型系统的程序时。在C语言中,可以使用强制类型转换(cast)来允许以一种数据类型引用一个对象,而这种数据类型与创建这个对象时定义的数据类型不同。

  4. 表示字符串
    • C语言中字符串被编码为一个以null(其值为0)字符结尾的字符数组。每个字符都由某个标准编码来表示,最常见的是ASCII字符码。
  5. 表示代码
    • 不同类型的机器使用不同的且不兼容的指令和编码方式, 因此二进制代码是不兼容的。
  6. 布尔代数(Bool algebra)简介
    • 将逻辑值TRUEFALSE编码为1和0, 设计出一种代数
    • 将4个布尔运算扩展到位向量, 位向量就是有固定长度为w, 由0和1组成的串
  7. C语言中的位级运算
    • | OR, & AND, ~ NOT, ^ EXCLUSIVE-OR
  8. C语言中的逻辑运算
    • || && !,逻辑运算认为所有非零参数都表示为TRUE,而参数0表示FALSE
  9. C语言中的移位运算
    • 机器支持两种形式的右移:算术右移和逻辑右移,逻辑右移在左边补k个0, 算术右移是在左端补k个最高有效位的值

整数表示

用位来编码整数的两种不同方式:一种只能表示非负数,而另一种能够表示负数,零和正数

  1. 整数数据类型
    • C语言支持多种整数数据类型——表示有限范围的整数。
    • 一个与机器相关的取值范围是大小指示符long, 大多数64位机器使用8个字节表示, 32位机器上使用4个字节表示
    • 负数范围比整数的范围大1
  2. 无符号整数的编码
    • 我们用一个函数B2U(Binary bo Unsigned,长度为w, 向量为{x},如:[1011])来表示:
    • 最小值0, 最大值:
  3. 补码编码
    • 有符号数最常表示方式就是补码(two’s-complement), 用函数B2T(Binary to two’s-complement)表示:
    • 最小值是位向量[10…0]的值:
    • 最大值是位向量[010…0]的值:
    • 反码(Ones’ Complement): 除了最高有效位的权, 和补码一样:
    • 原码(Sign-Magnitude): 最高有效位是符号位, 用来确定负权还是正权:
  4. 有符号数和无符号数之间的转换
    • 数值可能会改变,但是位模式不变。
    • B2U和B2T都是双射, 就有明确的逆映射。
    • 函数U2T描述了从无符号数到补码的转换, 而T2U描述的是补码到无符号的转换
    • ,如果令
    • , 得到:
    • 推导一个无符号树u和与之对应的有符号数U2T(u)之间的关联。设
    • , 得到:
  5. C语言中的有符号数与无符号数
    • C语言支持所有整形数据类型的有符号和无符号运算, 转换的规则是底层的位表示不变
    • 有符号数和无符号数运算时, C语言隐式地将有符号数强制转换为无符号数
  6. 扩展一个数字的位表示
    • 在不同字长的整数之间转换, 同时又保持数值不变
    • 将一个无符号数转换为一个更大的数据类型, 只需简单地在表示的开头添加0, 这种运算称为零扩展(zero extension)
    • 将一个补码数字转换为一个更大的数据类型, 在表示中添加最高有效位的值的副本, 称为符号扩展(sign extension)
  7. 截断数字
  8. 关于有符号数与无符号数的建议
    • 有符号数到无符号数的隐式强制类型转换导致了某些非直观的行为,避免这类错误的一种方法就是绝不使用无符号数

第3章 程序的机器表级示

计算机执行机器代码, 用字节序列编码低级的操作。

历史观点

Linux使用了平坦寻址(flat addressing), 使程序员将整个存储空间看做一个大的字节数组。

程序编码

  1. 机器级代码
Yan Peipan 18 March 2015