第一章
简述
计算机系统是由硬件和系统软件组成的.
信息就是位+上下文
ASCII:
- SP : 32
- \n 10
系统中所有的信息, 都是由一串比特表示的.
一个简洁的设计最好由一个人而非一个协会掌控. (C语言)
C语言是设计用来实现Unix操作系统的, 是系统级编程的首选.
程序被其他程序翻译成不同的格式
每条C语句都必须被其他程序转化为一系列的低级机器语言指令,然后这些指令按照一种称为可执行目标程序的格式打好包,再以二进制文件的形式存放起来.
Unix上的编译指令:
linux> gcc -o hello hello.c
编译流程:
- 预处理阶段: 直接将预处理命令插入程序文本. 得到.i文件;
- 编译阶段: 编译器将.i翻译为.s, 其由汇编语句构成.
- 汇编阶段: 汇编器将.s文件翻译成机器语言指令, 并将它们打包成可重定位目标程序的格式, 其后缀为.o;
- 链接阶段: 为程序链接调用的各种外部函数. 最后得到可执行文件.
了解编译系统如何工作是大有益处的
问题:
- 一个switch语句是否总比一系列if-else语句高效?
- 函数调用的开销有多大?
- while循环比for循环更有效?
- 指针引用比数组引用更高效吗?
- …
学习安全编程的第一步就是理解数据和控制信息存储在程序栈上的方式会引起的后果.
处理器读并解释储存在内存中的指令
系统的硬件构成
总线: 通常总线被设计成传送定长的字节块, 也就是字(word), 字长是基本的系统参数, 各个系统中都不尽相同(通常为4个字节(32bits)或8个字节(64bits)).
处理器: 处理器的核心是一个大小为一个字的储存设备(或寄存器), 称为程序计数器(PC). 在任何时刻, PC都指向主存中的某条机器语言指令(指令地址).
从系统通电开始, 直到系统断电, 处理器一直在不断的执行程序计数器指向的指令, 再更新程序计数器, 使其指向下一条指令. 处理器指令模型称为指令集架构.
除此之外, ALU(算数/逻辑单元)用来进行算术和逻辑运算.
指令集架构描述的是每条机器代码指令的效果, 而微体系结构描述的是处理器实际上是如何实现的.
运行hello程序时, 计算机到底做了什么?
高速缓存至关重要 & 储存器的层次结构
L1和L2高速缓存使用SRAM, 效率比DRAM高几十倍.
LO是寄存器, 速度最快, 造假最高, 而L4为主存, L5为本地磁盘, L6则是远程储存, 同时单位造价相对更低.
操作系统管理硬件
操作系统连接客户和系统, 其有两个基本功能:
- 防止硬件被失控的应用程序滥用;
-
向应用程序提供简单一致的机制来控制不同的低级硬件设备.
操作系统通过进程, 虚拟内存, 文件来实现这两个功能.
进程
使用程序时程序看上去在独占处理器, 主存和I/O设备, 这种假象是通过进程的概念来实现的.
通过并发运行, 可以实现这种假象.
并发运行通过进程的上下文切换来实现交错执行, 从一个进程到另一个进程的转换是由操作系统内核管理的.
线程
一个进程可以由多个成为线程的执行单元组成, 每个线程都运行在进程的上下文中, 并共享同样的代码和全局数据.
虚拟内存
虚拟内存让每个进程都独占地使用主存. 每个进程看到的内存都是一致的, 如下图所示:
其中堆可以在运行时动态地扩展和收缩.
编译器使用栈实现函数待用.
Amdahl定律
当我们对系统的某个部分加速时, 其对系统整体性能的影响取决于该部分的重要性和加速程度. 要想显著加速整个系统, 必须提升全系统中相当大的部分的速度.
若系统执行某应用程序需要时间T_{old}
,假设系统某部分需要的执行时间与T_{old}
的比例为\alpha
, 而该部分性能提升比例为k
, 则总的执行时间应为:
T_{new} = (1-\alpha)T_{old}+(\alpha T_{old})/k = T_{old}[(1-\alpha_\alpha /k)]
由此, 可知加速比为:
S=\frac{T_{old}}{T_{new}}=\frac{1}{(1-\alpha)+\alpha/k}