大杂烩
-
平台:IA-64 + linux + C + gcc
-
检测引用错误的工具(例如 Valgrind)
-
在 linux 系统中:gdb
在 Macintosh OS 上:lldb -
很多开源库都用 size_t 类型的循环计数器
-
操作系统:管理进程状态
-
系统为每个 进程 提供私有地址空间
-
编译
-
预处理程序
-
处理 #include 指令(包含头文件)
-
处理 #define 指令(宏定义替换)
-
处理条件编译 #if, #ifdef
- 输入:.c 或 .cpp 源文件
输出:预处理后的源文件(.i)
-
-
编译器:将预处理后的源代码翻译成 汇编代码
- 输入:预处理后的源文件 (.i)
输出:汇编代码文件(.s)
- 输入:预处理后的源文件 (.i)
-
汇编器:将汇编代码转换成 机器码
-
输入:汇编代码文件 (.s)
输出:目标文件(.o 或 .obj) -
缺少不同文件间代码的链接信息
-
-
链接器:将多个目标文件和库文件连接成最终的 可执行文件
-
输入:一个或多个目标文件 (.o) + 库文件
输出:可执行文件(在 Windows 是 .exe,Linux 无扩展名) -
解析不同文件之间的 引用关系
与静态运行时库 合并
(例如 malloc、printf 的代码)
部分库采用 动态链接
链接过程在程序开始执行时进行
-
-
-
反汇编器
-
volatile 告诉编译器
-
这个变量可能被意外修改
(比如硬件、其他线程、或越界写) -
所以不要对它做优化
(比如缓存到寄存器、重排读写顺序) -
必须严格从内存中读写它的值
-
-
异常控制流
-
程序正常的执行流程被中断或改变
-
包括硬件中断、系统调用、进程切换等
-
信号是其中一种软件层面的实现方式
-
信号是操作系统提供的一种 进程间通信机制
用于通知进程发生了某个事件 -
异步发生:信号可以在程序执行的任何时刻到达
-
软件实现:由操作系统内核管理和传递
-
改变控制流:当信号到达时,程序会暂停当前执行,跳转到信号处理函数
-
-
固定转移
远距离转移
-
存储
-
存储器
硬件,高速缓冲存储器,dram 的层次结构 -
dram
-
计算机、服务器的主内存,显卡的显存
-
主存储器 主要由 DRAM(动态随机存取存储器)构成
-
-
ddr
-
通信标准/协议
-
规定了 内存控制器(通常在CPU内)如何与 DRAM芯片 对话
即如何传输 数据、地址和控制命令 -
在每个时钟周期的 上升沿和下降沿 都传输一次数据
-
-
cpu
-
PC(程序计数器)
存储 下一条要执行的指令 的地址
在 x86-64 架构中,被称为 RIP -
寄存器组
用于存储程序中频繁使用的数据 -
条件码
存储最近一次算术或逻辑操作的状态信息
为条件分支(如 if 判断、循环)提供依据
-
-
内存
-
内核空间(高地址):操作系统内核专属区域,隔离用户程序
-
栈区:向下增长(高位地址 向 低位地址 扩展)
-
内存映射区:用于共享库、mmap 分配等动态区域
-
堆区:向上增长(低位地址 向 高位地址 扩展)
-
bss段:存储未初始化的全局 / 静态变量
-
数据段:存储初始化的全局 / 静态变量
-
rodata段:存储只读数据(如字符串字面量)
-
代码段(低地址):存储程序指令
-
-
内存 和 cpu
-
CPU 向 内存 发送 地址
-
内存 向 CPU 传输 指令
-
数据在 CPU 和 内存 之间双向传输
-
-
内存和磁盘
-
程序启动:磁盘→内存(加载数据)
-
磁盘先将程序的
可执行文件(如 word.exe)
文档的原始数据(如报告.docx)
读取到内存 -
CPU只能直接访问内存
(无法直接读取磁盘数据) -
CPU直接 从内存访问指令和数据
-
内存速度远快于磁盘
加载到内存后
程序才能快速响应操作
(如打字、排版)
-
-
运行中:内存→磁盘(保存数据)
-
程序运行时产生的 临时数据
会先 存放在内存 中-
如正在输入的文字
未保存的修改 -
速度快,适合实时交互
-
-
保存时
内存会将这些临时数据 写入磁盘
转为 永久存储
(即使断电,磁盘中的数据也不会丢失) -
若未保存就断电
内存中的临时数据会 丢失
(内存易失性)
但磁盘中之前保存的版本仍存在
-
-
空间不足:磁盘→内存(扩展缓存)
-
当内存容量不够时
(如同时开太多软件)
系统会启用 “虚拟内存” 技术 -
从 磁盘中划分 一块空间
当作 临时内存 使用- 如 Windows 的 页面文件
Linux 的 交换分区
- 如 Windows 的 页面文件
-
内存管理单元(MMU)负责地址转换
页面交换(Paging):将不常用的内存页 换出到磁盘 -
速度变慢,因为磁盘远慢于内存
-
-
常见性能问题
-
内存不足 → 频繁的磁盘交换 → 系统变慢
-
磁盘I/O慢 → 程序加载和数据访问延迟
-
内存与磁盘速度不匹配 → 整体性能受限
-
-
优化策略
-
增加内存:减少 磁盘交换频率
-
使用SSD:提高 磁盘访问速度
-
优化数据布局:提高 缓存命中率
-
预读取技术:提前将可能需要的数据 加载到内存
-
-
数据库工作流程
-
数据页从磁盘加载到内存缓存池
-
查询在内存中执行
-
事务提交时,脏页异步写回磁盘
-
日志机制确保数据一致性
-
-
操作系统调度
-
进程创建:程序代码从磁盘加载到内存
-
上下文切换:进程状态在内存中保存和恢复
-
文件缓存:常用文件数据缓存在内存中
-
-
-
数据类型,大小
![image]()
-
在 x86-64 架构中
long double 的实际有效数据长度是
10 字节(80 位扩展精度) -
但由于内存对齐要求
编译器会将其存储为
16 字节(128 位) -
这中间的 6 字节属于 “填充” 空间
-
SIMD指令支持
现代CPU的向量寄存器通常是16字节的倍数
16字节对齐便于使用SSE/AVX指令 -
缓存行优化
缓存行通常为64字节
规整的大小便于缓存管理 -
现代处理器的内存控制器和缓存系统
以 16 字节为基本读写单元
(尤其是支持 SSE/AVX 指令的处理器)
如果数据未对齐
CPU 需要多次访问内存并手动拼接数据
导致性能大幅下降
-
-
字与字节
-
内存的 最小寻址单位是 字节
-
每个字节 都有唯一的地址
-
字是比字节更大的存储单位
通常为 32 位或 64 位
一个字 包含多个字节 -
对于32 位字(占 4 字节),下一个字的地址比前一个字的地址大 4
对于64 位字(占 8 字节),下一个字的地址比前一个字的地址大 8
-
-
struct 内存对齐
-
成员对齐
-
结构体中每个成员的起始地址
必须是其自身大小的整数倍 -
若不满足,编译器会在成员之间插入填充字节
-
-
整体对齐
-
结构体的总大小必须是其最大成员大小的整数倍
-
若不满足,编译器会在结构体末尾插入填充字节
-
-
-
大端序(高位字节存低地址)
小端序(低位字节存低地址)-
大端序:可读性好,符合直觉
小端序:机器性能好,处理更快 -
x86 架构通常是小端序
-
所有网络数据包中的 32 位字
必须以大端序传输-
大端序将关键信息(如 IP 地址、端口号)的高位字节
置于数据包起始位置
接收方无需额外转换即可直接读取
解析效率更高 -
IPv4 头部的 协议类型 字段(1 字节)
TCP 头部的 源端口 字段(2 字节)
均按大端序排列
-
uint32_t value = 0x12345678; // 大端序 (网络字节序) 内存地址: 低地址 -> 高地址 字节顺序: 0x12 0x34 0x56 0x78 // 小端序 (x86/x64主机字节序) 内存地址: 低地址 -> 高地址 字节顺序: 0x78 0x56 0x34 0x12![006f322ca40fd8e936dab5082eba8186]()
-
ASCII 字符是 单字节编码
字符串由 “逐个单字节字符” 组成 -
多字节数据(如 int、double)
会受 “大端 / 小端” 字节序影响 -
单字节的字符不存在 “字节序反转” 问题
-
-
缓存 Cache
-
速度远快于主存(如 DDR 内存)
-
容量较小
-
没有操作缓存的指令
无法直接访问缓存 -
命中
CPU 访问数据时
若缓存中已存在该数据
直接从缓存读取 -
失效 / 缺失
缓存中无该数据
需从主存加载数据到缓存- 需等待主存响应
性能下降
- 需等待主存响应
-
缓存行 Cache Line
-
缓存 与 主存 的数据交换单位
通常大小为 64 字节 -
缓存由 缓存行 组成
-
若 CPU 需要当从主内存中读取数据时
不仅会读取请求的数据
还会将其附近的数据一起 读入缓存行- 相邻内存地址 的数据
会被一起加载到 缓存
- 相邻内存地址 的数据
-
-
多级缓存
-
l1
-
分为 指令缓存(I-Cache) 和 数据缓存(D-Cache)
-
优先存储最常用的指令 / 数据
-
CPU 内核内
-
每个核心独有
-
-
l2
-
比 L1 慢,但更大
-
缓存 L1 未命中的数据
-
CPU 内核外
-
每个核心独有
-
-
l3
-
比 L2 慢,但更大
-
缓存 L2 未命中的数据
-
所有核心共享
-
![4853474114b63c06f6ce4cc0d8ab944c]()
-
-
组织结构
-
直接映射:每个主存块 只能映射到缓存中的 一个特定位置
-
内存块地址 MOD 缓存中的总行数
-
硬件简单,查找速度快
-
冲突率高
如果两个频繁访问的内存块恰好映射到同一个缓存行
它们会不断地相互驱逐
导致 缓存抖动
性能急剧下降
-
-
全相联:每个主存块 可以映射到缓存中的 任意位置
-
冲突率最低
缓存空间的利用最充分 -
硬件复杂,速度慢
-
-
组相联:每个主存块 可以映射到缓存中的 一组中的任意一个位置
-
组的选择:内存块地址 MOD 组数
-
组内全相联
- 若组内已满,则通过替换算法(如 LRU)在组内选择一个缓存块替换
-
N 路组相联:每组包含的行数 N
-
- 小容量缓存(如TLB)采用 全相联
x86 采用 组相联
-
-
加载 新数据,淘汰 旧数据
- 缓存容量不足时
-
LRU:淘汰 “最近最少使用” 的数据
-
FIFO(先进先出):淘汰 最早进入缓存 的数据
-
随机淘汰
- 性能较差但实现简单
-
缓存数据的写入
- 当 CPU 修改 缓存 中的数据时
需同步更新 主存
-
写直达:写缓存的同时立即写主存
- 数据一致性强
- 速度慢
-
写回:仅写缓存,当该数据被 淘汰 时再写回主存
- 速度快
- 需 脏位 标记是否修改过
- 当 CPU 修改 缓存 中的数据时
-
多核
需处理 缓存一致性 问题
(如 MESI 协议)
避免不同核心缓存数据冲突![image]()
-
行优先
有利于缓存性能
因为CPU缓存可以预取连续的内存块
减少缓存缺失 -
如果程序访问的内存地址连续 / 邻近
缓存命中率高
能大幅减少慢内存的访问次数
提升效率
-
-
缓存和虚拟内存的效应会极大地影响程序性能
-
虚拟地址空间由机器字长决定
32位机,地址是 4 字节
64位机,地址是 8 字节-
64 位机的 “64 位” 核心指
数据总线 / 地址总线宽度 -
64 位 CPU(如 x86-64、ARMv8-A 架构)
的算术逻辑单元(ALU)
原生支持 64 位数据运算 -
x86-64架构
通常使用48位虚拟地址空间
用户空间通常使用47位有效地址
最高位用于区分 用户/内核 空间 -
地址的规范形式
低 48 位(位 0~位 47)作为有效地址位
高 16 位(位 48~位 63)必须是位 47 的 “符号扩展”
(即高 16 位与位 47 的值完全一致) -
地址空间划分
// 典型的x86-64虚拟地址布局 用户空间: 0x0000000000000000 - 0x00007FFFFFFFFFFF // 47位有效 内核空间: 0xFFFF800000000000 - 0xFFFFFFFFFFFFFFFF // 47位有效-
内存的最小寻址单位是 字节,每个字节都有唯一的地址
-
gcc 可以指定64位编译或32位编译
-
64 位系统环境下
GCC 默认会编译生成 64 位程序 -
若要生成 32 位程序
需使用 -m32 选项
但前提是系统已安装 32 位编译工具链
(如 Linux 需安装 gcc-multilib 包
Windows 下 MinGW 需配置 32 位工具链)
-
-
整数
-
集合的表示
- 位向量 01101001 表示集合
-
逻辑运算支持 提前终止(短路求值)
-
移位
- 左移
丢弃左侧溢出的额外位
右侧用 0 填充
-
逻辑右移
丢弃右侧溢出的额外位
左侧用 0 填充 -
算术右移
丢弃右侧溢出的额外位
左侧复制最高有效位(符号位) -
CPU中移位指令
位移量要小于被位移数的位数
比如左移数A(假设8bit)
A << 10
左移的位数大于数本身的位数
10 mod 8 = 2
即左移2位 -
在 x86 架构中:
SHL reg, cl 指令
实际使用的是 CL 寄存器的低 n 位
n 取决于寄存器宽度:-
8 位寄存器:取 CL & 0x07(mod 8)
16 位寄存器:取 CL & 0x0F(mod 16)
32 位寄存器:取 CL & 0x1F(mod 32)
64 位寄存器:取 CL & 0x3F(mod 64) -
在汇编里写 mov al, 1; shl al, 10
实际上会左移 10 mod 8 = 2 位
-
- 左移
-
数字的表示
- 按位取反运算符 ~
- 有符号整数:补码(5 变成 -6,127 变成 -128)
- 无符号整数:最大无符号值 − 原值
![image]()
TMAX 与 TMin 互为补码
∣TMin∣ = TMax + 1
UMax = 2 × TMax + 1
(左移一位,右侧补零,加 1 把右侧的 0 变为 1)
![image]()
有符号数 ←→ 无符号数
![image]()
死循环
无符号数永远 不会小于 0for (size_t i = n; i >= 0; --i){ func(arr[i]); }无符号数作为索引
for (size_t i = cnt - 2; i < cnt; --i) a[i] += a[i+1]; -
当一个表达式中混合了 有符号和无符号 类型时
有符号值会隐式转换为 无符号值
再进行 运算 或 比较 -
符号扩展
有符号整数:w 位 → w + k 位
复制 k 次符号位 -
截断
-
都是丢弃高位,保留低位
无符号:x mod 2 ^ k,k 为要保留的位数
有符号:结果会被重新解释为有符号值,可能出现 正变负,负变正 -
无符号整数加法
计算机硬件存储无符号数时
位宽固定为 w
最终结果是 (u + v) mod 2^w
数学加法 + 可能加上或减去 2^w -
有符号整数加法
数学加法 + 可能加上或减去 2^w-
正溢出:若和 ≥ 2^(w-1)
(4 位下为 ≥ 8)
结果会变成负数 -
负溢出:若和 < -2^(w-1)
(4 位下为 < -8)
结果会变成正数
-
-
无符号整数乘法
u 和 v 都是 w 位
结果为 uv mod 2 ^ w -
整数除法
向 0 取整
-
-
整数乘法与移位
编译器 可能会解释为移位
当乘数是 2 的幂 时,乘法可被等价转换为左移 -
求相反数
取反加一
简单证明:x + ~x = 111....111 = -1-
x 和 y 都是 int 类型的
已知 x > y
不能得出 -x < -y -
特例
int a = -2147483648; assert(a == -a); -
-
掩码
二进制数,按位与
筛选 出目标数据的 特定位
限制 数值范围-
非负整数 n
n & 7 == n % 8
按位与 远快于 取模 -
需要保留的位,则和 1 相与
需要清除的位,则和 0 相与
-
浮点数
-
![image]()
-
计算机只能精确表示
形如 x / (2 ^ k) 的数
x 是整数,k 是正整数 -
十进制小数:乘以 2^n
其二进制表示:小数点右移 n 位 -
定点数:小数点位置固定
浮点数:阶码范围有限 -
若要表示极小数,如 10^(-300)
可能因位数不足 下溢 为 0
若要表示极大数,如 10^300
可能因位数不足 上溢 为无穷大 -
浮点数的数学表达式
二进制的科学计数法
![image]()
-
符号位 s
-
有效数 M
通常是规范化的小数
范围在 [1.0, 2.0) 之间 -
指数 E
范围是 - 126 到 + 127 (单精度)
范围是 - 1022 到 + 1023 (双精度)
-
-
编码
![image]()
-
浮点数通过三个二进制字段存储
-
符号位 s
-
指数字段 exp
-
小数字段 frac
- 只存储 M 的小数部分
![image]()
-
-
double (8 字节 64 位)
1 位 s
11 位 exp
52 位 frac
d7 ... d4:高 4 字节(对应浮点数的高位部分,如 exp 和 frac 的高位)
d3 ... d0:低 4 字节(对应浮点数的低位部分,如 frac 的低位) -
float(4 字节 32 位)
1 位 s
8 位 exp
23 位 frac -
规格化值
-
exp 的所有位的值
既不能 全是 0
也不能 全是 1 -
exp = E + 固定偏置量
-
用 无符号数 来同时表示 正、负指数
-
单精度中,8 位指数字段的偏置量为 127
双精度中,11 位指数字段的偏置量为 1023 -
单精度中,exp 的范围: 1 到 254
双精度中,exp 的范围: 1 到 2046
-
-
-
非规格化值
-
exp 为全 0
-
exp = E + 固定偏移值 ❌
1 = E + 固定偏移值 ✔-
非规格化数的 E 为 -126(单精度)
和规格化数的 E 的最小值相等 -
最大的非规格化数(frac全 1 时)
刚好小于 最小的规格化数 -
保证了数值从 0 到最小规格化数的 连续性
-
-
规格化数, M 整数部分为 1
非规格化数,M 整数部分为 0 -
若 exp = 000…… 且 frac = 000……:表示零值。
- 注意存在不同取值:+0 和 -0
-
若 exp = 000…… 且 frac ≠ 000……:表示最接近 0.0 的数
- 这些数是等间距的
-
-
特殊值
-
exp 全为 1
-
若 exp = 111…… 且 frac = 000……:∞
-
包含正无穷和负无穷
-
1.0 / 0.0 = -1.0 / -0.0 = +∞
-
1.0 / -0.0 = -∞
-
-
若 exp = 111…… 且 frac ≠ 000……:NaN(非数)
- √-1,∞ - ∞,∞ × 0
-
-
示例
![image]()
-
0 附近精度更高
-
小值密集、大值稀疏
-
-
舍入模式
-
向零舍入
-
向下舍入,朝向负无穷
-
向上舍入,朝向正无穷
-
四舍六入五凑偶
-
IEEE 浮点数的默认舍入模式
-
平衡舍入偏差,让误差更均匀
-
偶:最低有效位为 0
-
五:舍入位置右侧的位为 100……
![image]()
-
-
-
浮点数的加法的性质(对比 阿贝尔群)
-
对加法封闭
- 但可能产生 ∞ 或 NaN
-
可交换,但不满足 结合律
- 溢出与舍入的不精确性
-
每个元素都有加法逆元
- 无穷大和 NaN 除外
-
若 a >= b,则 a + c >= b + c
- ∞ 和 NaN 除外
-
-
浮点数乘法的性质(对比 交换环)
-
对乘法封闭
- 但可能产生 ∞ 或 NaN
-
可交换,但不满足 结合律
-
乘法单位元是 1
-
乘法对加法 不满足 分配律
-
若 a >= b 且 c >= 0,则 a × c >= b × c
- ∞ 和 NaN 除外
-
-
浮点数加法(数学表达式)
-
小数点对齐,使指数匹配
-
E 大的数不动
-
E 小的数,M 的小数点右移 k 位
- k 为两个数的 E 的差的绝对值
-
-
符号处理与相加
-
s 不同
-
s' 与 M 较大的数 的 s 相同
-
M' = 两个 M 的差的绝对值
-
-
s 相同,E 相加
-
-
规格化
-
若 M > 2
-
有效数溢出一位
-
M 的小数点左移一位
-
E 加 1
-
-
若 M < 1
-
M 的小数点右移 k 位,直到 1 <= M < 2
-
E 减 k
-
-
-
-
浮点数乘法(数学表达式)
-
精确结果
-
s = s1 异或 s2
-
M = M1 × M2
-
E = E1 + E2
-
-
调整
-
若 M > 2,将 M 右移,并对 E 加 1
-
若 E 超出范围,发生溢出
-
对 M 舍入,以适配 frac 的精度
-
-
浮点数与整数
-
int, float, double 类型转换
-
double/float 转换为 int
- 截断小数部分
绝对值变小
类似于 向零舍入
- 截断小数部分
-
int 转换为 double
- 不会有精度损失
-
int 转换为 float
- 当整数的 绝对值 大于 2 ^ 24 时
转换会有精度损失- 因为 float 的 frac 23位
- 当整数的 绝对值 大于 2 ^ 24 时
-
-
x² >= 0
-
x 是 float 类型
x 为 NaN 时 ❌
其他情况 ✔ -
x 是 int 类型
是整数模环
-2的31次方 到 2的31次方-1
可能会溢出
-
-
(x + y) + z == x + (y + z)
-
int 或 unsigned int
正确
为什么 -
float 类型
float a = 1e20; float b = 3.14; cout << (a - a) + b << endl; cout << a + (-a + b) << endl; // 第一个 3.14,第二个 0 -
-
除法不能用流水线
汇编
-
x86-64(又称 x64、AMD64)
是指令集架构(ISA)
是 x86 架构的 64 位扩展
是 指令集的 64 位版本-
地址转换 仍依赖 分页 机制
(4KB/2MB/1GB 页)
但新增 大页 支持
减少页表层级
提升地址转换效率 -
扩展 虚拟地址空间
-
扩展 寄存器 数量 和 位宽
-
指令集增强
- 兼容 32 位 x86 的 指令
- 64 位新增指令
- 专用指令扩展
-
-
寄存器
-
64 位 通用寄存器
![image]()
-
可用通用寄存器总数从 8 个增加到 16 个
减少了 函数调用时 对栈内存的访问次数
显著提升了性能 -
栈指针 RSP
默认按 8 字节对齐
-
-
向量寄存器 / SIMD寄存器
-
SSE 系列
16 个 128 位 XMM 寄存器(XMM0~XMM15)
浮点运算、整数向量操作 -
AVX 系列
256 位 YMM 寄存器(YMM0 - YMM15)
512 位 ZMM 寄存器(ZMM0 - ZMM31) -
cpu-z
支持 XMM 寄存器 ← 包含 SSE/SSE2/SSE3/SSSE3/SSE4.1/SSE4.2
支持 YMM 寄存器 ← 包含 AVX/AVX2
支持 ZMM 寄存器 ← 包含 AVX-512
![image]()
-
-
指令指针 RIP
64 位版本的 IP(指令指针)
存储下一条要执行的 指令地址
支持 64 位 虚拟地址寻址 -
标志寄存器 RFLAGS
在 32 位 EFLAGS 基础上扩展
新增 地址大小位(AD),操作数大小位(OP)
用于切换 32/64 位 地址 / 操作数 模式-
ZF 零标志:计算结果为 0 时,ZF 为 1,否则为 0
-
OF 溢出标志:有符号数,运算时结果溢出,OF 为 1
-
CF 进/借位标志:无符号数,加法进位 or 减法借位时,CF = 1
-
SF 符号标志:符号位为 1,SF = 1
-
DF 方向标志:控制字符串操作的方向
-
IF 中断使能标志
-
-
控制寄存器(CR0-CR4、CR8)
-
控制 CPU 工作模式
(如保护模式、分页) -
缓存策略
-
-
调试寄存器(DR0-DR7)
-
硬件调试
-
设置断点
-
监视内存访问
-
-
段寄存器
-
cs:指向代码段(存储程序指令的内存区域)
-
ds:指向数据段(存储全局 / 静态数据的内存区域)
-
ss:指向栈段(存储栈数据段,函数调用上下文的区域)
-
es:扩展数据段
-
fs:
-
gs:
-
-
-
64 位版本的 Intel 指令集
英特尔处理器的汇编语言-
Intel 语法(Windows 常用)
指令 目的操作数, 源操作数 -
AT&T 语法(Linux / GCC 常用)
指令 源操作数, 目的操作数-
寄存器名前加 %
-
立即数前加 $
-
指令后缀:b/w/l/q 表示操作数宽度
b(byte,1 字节)
w(word,2 字节)
l(long,4 字节)
q(quadword,8 字节)
-
-
数据传输的方向
寄存器 ↔ 寄存器
寄存器 ↔ 内存
立即数 → 寄存器 / 内存- 不能直接从内存到内存
(必须经过寄存器中转)
- 不能直接从内存到内存
-
算术与逻辑操作
addq %rbx, %raxrax += rbx
subq %rbx, %raxrax -= rbx
imulq %rbx, %raxrax *= rbx(有符号)
imulq %rbx%rax 是默认被乘数
divq %rbxrax = rax / rbx(无符号,商在 rax,余数在 rdx)
cmpq %rbx, %rax计算 rax - rbx,不保存结果,只影响标志位
andq %rbx, %raxrax &= rbx
orq %rbx, %raxrax |= rbx
xorq %rbx, %raxrax ^= rbx
notq %raxrax = ~rax(按位取反)
negq %raxrax = -rax(取负)
incq %rcx自增
incq (%rsp)栈顶内存值加 1
decq %rdx自减
decq 8(%rbp)该内存值减 1
shlq / salq:左移
shrq:逻辑右移
sarq:算术右移 -
控制转移
-
无条件跳转
jmp label:直接跳转到目标地址
callq func:函数调用
retq:函数返回 -
条件分支
je label# 相等(ZF=1)
jne label# 不相等(ZF=0)
jl label# 小于(有符号)
jg label# 大于(有符号)
jle label# 小于等于(有符号)
jge label# 大于等于(有符号)
![image]()
-
-
内存寻址
-
直接寻址
movl 0x1234, %eax -
寄存器间接寻址
movl (%ebx), %eax
从 EBX 指向的内存地址读取值到 EAX-
%ebx = 0x1000
内存地址 0x1000 处的值是 0x12345678 -
结果:%eax = 0x12345678
-
以 %ebx 的值 0x1000 作为内存地址
取出该地址的 4 字节(long)内容
放入 %eax -
相当于 temp = *ptr
-
-
基址 + 位移寻址
-
movq -8(%rsp), %rax
从地址 [RSP - 8] 读取值 -
movl 4(%ebp), %eax
从地址 [EBP + 4] 读取值
-
-
基址 + 索引寻址
-
movl (%ebx,%esi), %eax
从地址 [EBX + ESI] 读取值 -
movq (%rdi,%rcx), %rax
从地址 [RDI + RCX] 读取值
-
-
基址 + 索引 × 比例 + 位移
-
movl 8(%ebx,%esi,4), %eax
从地址 [EBX + ESI×4 + 8] 读取值 -
movq 16(%rdi,%rcx,8), %rax
从地址 [RDI + RCX×8 + 16] 读取值 -
比例因子只能是 1,2,4,8,不能是寄存器
-
-
-
加载有效地址(load effective address)
leaq 源操作数(地址模式), 目的寄存器-
movq (%rax), %rbx
会读取%rax指向的内存数据
存入%rbx(访问内存) -
leaq (%rax), %rbx
仅计算%rax的值(即地址本身)
存入%rbx(不访问内存,只做地址计算) -
取地址
int *p = &arr[3];
对应
leaq 12(%rdi), %rax -
计算算术表达式
leaq (%rdi,%rdi,2), %rax
计算3x-
由于地址模式中的D(Rb, Ri, S)
可以表示Rb + Ri×S + D
而S只能是 1、2、4、8、…… -
这恰好匹配很多算术表达式的形式
(如x + k*y,k=1/2/4/8)
因此leaq常被编译器用来快速计算这类表达式
-
-
-
System V AMD64 ABI
x86-64 Linux 环境下的函数参数传递约定- 第一个参数:%rdi
- 第二个参数:%rsi
- 第三个参数:%rdx
- 第四个参数:%rcx
- 第五个参数:%r8
- 第六个参数:%r9
-
-
微架构:架构的具体实现
示例:缓存大小和核心频率 -
机器码:处理器执行的字节级程序
汇编代码:机器码的文本表示形式
lab3
-
多字节数据(如函数地址、cookie 值)在内存中的字节排列顺序
-
栈帧的布局,包括
- 局部变量
- 返回地址
- 函数参数在栈中的存储位置
-
call指令压入返回地址
ret指令弹出返回地址并跳转的原理 -
缓冲区溢出的成因
- Gets/gets等函数无边界检查
- 输入过长字符串会覆盖栈上的返回地址
-
现代系统抵御攻击的两种核心机制
- 栈地址随机化(破坏代码注入的地址确定性)
- 栈内存不可执行(阻止注入代码运行)
-
绕过防护
-
面向返回编程(ROP)
-
代码注入攻击(CI)
- 通过缓冲区溢出覆盖函数返回地址
使其跳转到注入的自定义代码
再由注入代码调用目标函数并传递指定参数
- 通过缓冲区溢出覆盖函数返回地址
-
-
objdump -d
获取目标程序的汇编代码
分析函数地址、指令序列、gadget 位置 -
GDB
观察栈的实时状态
验证缓冲区溢出的覆盖范围
返回地址的修改效果
攻击字符串的执行流程 -
攻击字符串生成工具(HEX2RAW)
将十六进制格式的指令 / 地址 / 数据
转换为原始字节字符串
同时规避 0x0a(换行符)等非法字节
听课记录
-
用补码
- 不存在+0,-0
-
字长
-
数据的存储
每个字节有一个地址
连续
记其中一个地址
用最低的地址作为整个数据的地址
符号对齐
告诉我类型是什么 -
数值编码:正数的符号位0,负数的符号位1,剩余位作为数值部分,机器数的不变,真值
机器数:存在机器内的连同正负符号一起数码化的数
这个机器数的真值:它真正表示的数值
机器数比真值多一个符号位 -
存储器按字节编址
-
大端:msb所在的地址是
lsb -
面向内存编程
-
专用寄存器
-
标志寄存器 or 程序/状态字寄存器
-
x86:变长指令字
-
操作系统分配空间
-
将表达式编译为指令序列,计算机直接执行指令来完成运算
-
MIPS
-
sel
-
累加器al / ax / eax
-
linux 环境 和 gcc 工具链
操作系统

















浙公网安备 33010602011771号