CSAPP复习
Review
信息的表示和处理
信息的存储
- 字长:32/64位,取决于计算机的数据处理能力(寄存器大小)和内存寻址能力\(2^{32}=4GB/2^{64}=16GB\)。
- 字节序:

位运算、逻辑运算
位运算符/逻辑运算符
逻辑右移(补0)/算术右移(补最高位)
整数的表示
- 无符号数:\(B2U_w(\vec{x})\) \(\equiv\) \(\sum_{i=0}^{w-1}{x_{i}2^{i}}\)。
- 无符号数范围:\(0\)~\(2^{w}-1\)
- 有符号数(补码):\(B2T_w(\vec{x})\) \(\equiv\) \(-x_{w-1}2^{w-1}+\sum_{i=0}^{w-2}{x_{i}2^{i}}\)。
- 有符号数范围:\(-2^{w-1}\)~\(2^{w-1}-1\)
C语言中,两者混用,有符号数被转换成无符号数。(负数转换为大整数)
整数运算
- 整数扩展:无符号数(0扩展)/有符号数(符号位扩展)
- 整数截断:可能会丢失数据特性(正数截断变成负数)
- 有符号数计算相反数:按位取反加一(\(-2^{w-1}\)的相反数是本身)
- 有符号数加法:正溢出
- 无符号数加法:正/负溢出
- 有符号数/无符号数乘法:\(w\)位的数相乘需要用\(2w\)位来保存
- 移位与乘除法的关系:左移对应除法,右移对应乘法(负数需要加上一个偏置)
浮点数
用\(V\) = \((-1)^{s}×M×2^{E}\)的形式来表示一个数:
- 符号(sign)——s。
- 尾数(significand)——M,二进制小数,范围是1到2-\(ε\),或者是0到1-\(ε\)。
- 阶码(exponent)——E,对浮点数加权,权重是2的E次幂(可能是负数)。
将浮点数的位表示划分为三个字段分别进行编码:
- 一个单独的符号位s直接编码符号s。
- k位的阶码字段exp = \(e_{k-1}…e_{1}e_{0}\)编码阶段E。
- n位小数字段frac = \(f_{n-1}…f_{1}f_{0}\)编码尾数M。(依赖阶码字段的值是否等于0)
- 规格化数:

- 非规格化数:

- NaN/无穷:

- 上/下溢出:正负溢出/0溢出
浮点数运算
- 向偶舍入(round-to-nearest)
- C语言类型转换(不能丢失精度)
程序的机器级表示
数据访问指令MOV
- 寄存器:16个整数寄存器(注意参数的使用顺序:%rdi->%rsi->%rdx->%rcx/栈指针%rsp/返回值存入%rax中)
- 寻址方式:\(Imm(r_b, r_i, s)\)被计算为\(Imm+R[r_b]+R[r_i]·s\)
- MOV/MOVZ(zero)/MOVS(sign)
算术和逻辑操作
- 地址计算指令LEA(LEA只计算地址,不做内存访问)
- 单操作数指令/一元操作
- 双操作数指令/二元操作
控制
- 条件码:CF进位/ZF零/SF符号/OF溢出
- 修改条件码:CMP S1 S2(S2-S1)/TEST S1 S2(S1 & S2)
- 访问条件码:SET Dest(有符号greater/less//无符号above/below)
- if-else:跳转指令JMP/条件传送指令CMOV(处理器无需预测测试的结果就可以执行条件传送。处理器只是读源值,检查条件码,然后要么更新目的寄存器,要么保持不变。)
- 循环:while/dowhile/for
- switch:跳转表/间接跳转指令jmp *.L4(,%rdi,8)/优势(case分布密集时效率高)
过程
栈
- 从高地址向低地址生长
- %rsp为栈指针
- PUSH/POP指令
过程调用与返回
- 转移控制:过程调用指令CALL/过程返回指令RET
- 数据传送:参数传递——前六个使用寄存器,第七个开始用栈传递。
内存管理
- 栈帧

- 寄存器使用管理:调用者保护caller saved/被调用者保护callee saved
递归的实现
与普通调用函数没有区别/函数调用不破坏其他函数的数据/后进先出
数组
- 一维数组:知道起始地址、下标、数据宽度求任意元素的地址
- 高维数组:行优先存储/矩阵
- 多层数组:额外的存储空间/内部的数组长度可以不一致
结构体
- 结构体的内存布局:一块连续的内存区域/按顺序存储
- 字节对齐:若数据类型需要K(2、4、8)个字节,则该数据类型的地址必须是K的整数倍/结构体对齐:数据类型中最长字节的整数倍。(节省空间:按字节长度从小到大排)
缓冲区溢出
- 产生原因:边界检查/不安全的标准库函数
- 攻击:代码注入攻击(CI)/面向返回编程攻击(ROP)
- 防御:栈随机化/栈破坏检测:金丝雀值/限制可执行代码区域
处理器体系结构
流水线
- 基本原理:提高了系统的吞吐量(throughput),也就是单位时间执行的指令条数,但会轻微地增加延迟(latency),也就是从头到尾执行一条指令所需要的时间。
- 吞吐量 = 1/延迟
- 五阶段流水线:IF(包含更新PC)-ID-EXE-MEM-WB。
- 流水线的局限性:不一致的划分(受限于最慢的阶段)/过深的流水线导致收益下降成本上升(划分过多,更频繁访问寄存器)
存储器层次结构
存储技术
磁盘:
- 结构:盘片/面(两面)/磁道(同心圆)/扇区(由间隔隔开)
- 容量:磁盘容量 = \(\cfrac{字节数}{扇区} × \cfrac{平均扇区数}{磁道} × \cfrac{磁道数}{表面} × \cfrac{表面数}{盘片} × \cfrac{盘片数}{磁盘}\)
- eg:假设我们有一个磁盘,有5个盘片,每个扇区512字节,每个面20000条磁道,每条磁道平均300个扇区。则\(磁盘容量 = 512 × 300 × 20000 × 2 × 5 = 30.72GB\)
- 容量:记录区:半径由小到大为记录区0、1……(同记录区扇区数相同)
- 扇区访问:寻道时间/旋转时间/传送时间
趋势:处理器和存储器的性能都在提升/处理器与存储器之间的性能差距越来越大
局部性原理
时间局部性:被引用过一次的内存位置很可能在不远的将来再被多次引用。
空间局部性:如果一个内存位置被引用了一次,那么程序很可能在不远的将来引用附近的一个内存位置。
*存储器层次结构
金字塔结构:
缓存:命中/未命中(冷/冲突/容量)
高速缓存
- 高速缓存结构:

- 地址解析:组索引->标记->块偏移
- 直接映射(每组一行,速度快,冲突未命中频繁,不灵活)/组相联(每组多行,块替换算法LRU/LFU/Random)/全相联(一组多行,速度慢,最灵活,不适合大规模)
- 评价指标:未命中率 = 1 - 命中率
- 平均访问时间 = 命中时间 + 未命中率 * 未命中惩罚
- 写策略:写命中/写未命中

- 策略组合:效率优先(只在缓存中修改)/可靠性优先(全都修改)

链接
基本概念
- 编译过程:预处理、编译、汇编、链接
- 为什么需要链接/链接的作用:模块化/提高构建程序的效率
- 符号:全局符号(没有使用static修饰的全局变量与函数)/外部符号(对全局符号的引用)/局部符号(使用static修饰的全局变量与函数/生存期为全局,作用域为当前模块/局部符号不是局部变量)
- 符号表:描述当前ELF文件中定义的和引用的符号

静态链接
- 静态链接的过程:符号解析、重定位
- 符号解析:将每个引用(外部符号)与其它可重定向目标文件的符号表中的符号定义关联起来
- 强符号:已初始化的全局变量和函数/弱符号:未初始化的全局变量
- 解析规则:1. 多个同名强符号,链接报错2. 一个强符号和多个弱符号同名,选择强符号3. 如果有多个弱符号同名,从这些弱符号中任意选择一个
异常控制流
x86的异常处理
异常是由于处理器状态的改变而将控制权转移到操作系统内核
- 处理器工作状态:用户模式(普通进程)/内核模式(操作系统,对系统所有资源具有完全的访问权限)
- 异常处理程序运行在内核模式下
- 异常处理程序入口地址:异常表(操作系统生成异常表完成异常的处理程序的注册)/异常表基地址(操作系统将异常表基地址写入异常表基地址寄存器)/异常号
- 异常返回:执行异常返回指令/从内核模式返回用户模式
异常分类
- 异步异常:中断:外部设备通过处理器芯片中断引脚的电平变化触发/异常返回后执行断点处的下一条指令
- 同步异常:
- 陷阱:eg:系统调用、断点/异常返回后执行断点处的下一条指令/又名:软中断
- 故障:eg:缺页故障、内存访问权限故障、除0/有可能恢复/重新执行引起故障的指令或者终止
- 终止:eg:非法指令、内存奇偶校验错误/不可恢复/不返回
虚拟内存
物理和虚拟寻址
- 直接使用物理地址寻址:无法实现真正的多任务,无法实现任务间的内存地址隔离
- 使用虚拟地址寻址:内存管理单元MMU(将虚拟地址翻译成物理地址)
- 通过引入虚拟地址与物理地址的转换可以解决三个问题
-
- 每个进程有自己的连续平坦的私有地址空间
-
- 进程对指令/数据存储和访问不会互相影响支持多任务操作系统,实现进程空间的地址隔离
-
- 地址翻译的过程对程序员透明,每一次内存加载和存储都会进行地址翻译
地址空间
- 线性地址空间
- 虚拟地址空间(每个进程都有独立的虚拟地址空间)
- 物理地址空间(一个物理地址可能会被多个虚拟地址空间中的地址所映射/一个虚拟地址可能被映射为一个物理地址,可能没有对应的物理地址)
虚拟内存作为缓存的工具
意义:使内存作为下一层次存储(如:磁盘)的缓存,更有效率的使用内存
虚拟内存的组织:背景:未命中惩罚代价过高(磁盘访问)
以页(page)为单位分块,页大小,虚拟页、虚拟页号,物理页、物理页号(页表?)
- 全相联:使用页表记录映射规则
- 页表条目:记录某个虚拟页与物理页的映射关系
- 有效标识:1:已缓存:虚拟地址已分配,且加载到内存中
- 有效标识:0:未分配:虚拟地址没有被进程所申请和使用/未缓存:虚拟地址已分配,但没有加载到内存中
虚拟地址访问:
- 页命中:虚拟地址所对应的页表条目状态位已缓存/直接翻译为物理地址
- 页未命中(缺页):引发缺页故障异常,并由操作系统进行处理/如处理成功,从磁盘加载数据并更新页表,异常返回。异常返回后,重新访问虚拟地址,进行页命中的处理/如为未分配状态,则缺页异常处理程序会终止当前进程
- 工作集:一个进程在一段时间内只使用有限的虚拟内存页,这些页被称为当前进程的工作集/如果工作集小于物理内存大小时,进程在加载后经过一系列必须的缺页故障异常后会有非常好的运行效率/如果工作集大于物理内存,则会频繁引起容量未命中,程序的性能有比较大的下降/虚拟内存机制使得在较小物理内存下运行较大内存开销的程序成为了可能,虽然性能较差
页替换算法:需要更加精密复杂的页替换算法/由操作系统实现
写策略:主要采用回写策略而非直写策略
简化内存管理
- 主要思想:每个进程都有独立地虚拟地址空间(不同的虚拟地址空间使用不同的页表进行映射/每个进程都有一个自己的页表)
- 简化了内存的申请:分配新的虚拟页
- 简化了数据/指令在多进程之间共享:多个空间的虚拟页映射至一个物理页/动态链接库共享/共享内存
- 简化了链接:链接分配地址时不需要考虑是否与其他进程冲突
- 简化了加载:加载器在工作时只是将ELF的代码段和数据段映射至当前进程的虚拟地址空间/加载器不进行数据的传输/所有的数据传输都是进入进程的入口地址后,产生的一系列缺页故障,在故障处理程序中实现真正的数据传输
提供内存保护的机制
页表条目中包含多个权限标识位,在地址翻译时进行检查:读/写/可执行(x86-64)【用于防止代码注入攻击】/特权用户
地址翻译
- 页命中:1.页表基地址寄存器存放物理地址x86中为CR3寄存器2. 根据虚拟页号和页表基地址计算出页表条目地址PTEA3. 根据PTEA从内存中加载页表条目4. 根据页表条目将VPN翻译为
PPN - 缺页:页表条目有效标识位0,引发缺页故障异常/故障处理异常完成后,重新访问虚拟地址
- TLB
- 每个虚拟地址访问会导致两次内存操作,性能低1. 加载页表条目2. 根据翻译后的物理地址访问数据
- 引入一个专用缓存,只缓存页表条目,以VPN为索引
- TLB命中:直接从TLB中获取页表条目,减少了一次内存访问
- TLB未命中:从内存/缓存中加载页表条目,同时缓冲至TLB



浙公网安备 33010602011771号