信息的储存
-
信息就是bit,对于文本字符,有通用的ascii标准,使用单字节整数来表示字符
-
Tips:ascii中只定义了128个字符,涵盖英文字母和部分标点。
而现代计算机系统通常使用变长编码(1~4字节)的UTF来确保支持全球化的字符,
在中文语境下还有GBK编码,支持更多中文字符
编译器的工作
-
从源文件到目标文件,在Unix系统上通常使用GNU‘s gcc工具链
-
-
预处理器(向源代码中插入include、宏定义、etc.)
-
编译器(将源代码转为汇编代码)
-
汇编器(将汇编代码转为特定的机器代码)
-
链接器(链接使用的外部库,e.g. printf.o)
Tips:常见的编译工具链有 GCC、Clang(适用Unix-like系统),MSVC(适用Windows系统)
-
处理器的执行架构
-
系统的硬件组成
包括总线(传输固定字长的数据)、I/O设备、主存、处理器(可以加载、储存、操作、跳转)
-
程序的运行,以运行Hello World为例
- 将程序的目标文件和数据从磁盘复制到主存
- 处理器开始执行HelloWorld.o中的机器代码
- 字符串“Hello World”被从主存复制到寄存器中
- 字符串从寄存器复制到显示设备上,最终输出
储存层次
-
高速缓存非常重要(L1、L2、L3 etc.),可以有效提升系统速度
-
系统的储存器是有层次的,从高速到低速,从小容量到大容量,每一级储存都是下一级储存的缓存
L0 register -> L1 cache -> L2 cache -> L3 cache -> memory -> local disk -> remote storage
系统提供的抽象表示
现代操作系统在运行程序时,程序并不会直接访问I/O和储存,而是通过操作系统提供的服务来调用(system call)
操作系统作为程序和硬件的中间层,将硬件抽象化,提供通用的机制便于程序调用,同时防止硬件被程序滥用
-
进程
为程序营造独占运行的假象,进程只负责运行,而操作系统负责资源调度,切换不同的进程(上下文切换)
在程序进行系统调用时,在用户态和内核态之间切换
-
线程
一个进程可以包含多个执行单元(线程),不同的线程共享进程的上下文,更易于数据交换,且切换的开支较小
-
虚拟内存
为程序营造独占主存的假象,每个程序看到的虚拟内存空间都是一样的,从上到下包括:
- 只读的代码和数据区
- 可读写的数据区,储存在程序中提前声明的全局/静态变量
- 运行时堆内存,由malloc创建,可动态放缩
- 共享库映射的内存区域,e.g. C标准库
- 用户栈,负责函数调用时储存上下文,以及储存运行函数时的局部变量
- 为内核保留的虚拟内存(只能通过系统调用进行操作)
虚拟内存提供了内存管理的便利,同时也起到了保护隔离作用,防止重要数据被破坏
-
文件
文件即字节序列,在类Unix系统中,所有I/O设备都被抽象为文件,向程序提供了统一的视图,隐藏了具体实现,通过简单地读写文件就可以操作I/O设备
Tips:在大多数Unix-like系统中,访问I/O设备的文件接口在/dev目录之下
重要概念
-
Amdahl定律
当我们对系统的某个部分加速时,其对整体性能的影响取决于该部分的重要性和加速程度
记该部分占总体的比例为,记该部分的加速比为,则有总体加速比
假设对系统的60%部分进行3倍的加速,可得系统总体加速比仅为1.67
即想要显著加速整个系统,必须提升全系统中相当大部分的速度
-
并发和并行
从系统层次由高到低,分别有
-
线程级并发
使用线程可以在一个进程中执行多个控制流,对于处理器的超线程而言,这涉及到处理器的某些硬件有多个备份,可以在单个周期内选择执行哪一个线程,当某一线程需要等待时,处理器就会迅速切换到另一线程,以达到并行的效果
-
指令级并行
现代处理器采用流水线(在后章介绍)等技术,在一个指令周期内同时执行多条指令,称为超标量
-
单指令、多数据并行
即SIMD指令,能够通过一个指令同时操作多个数据,可以使用支持的向量数据类型进行计算,使编译器自动优化为SIMD指令提高效率
-