80x86汇编编程-2(内存管理)
80x86汇编内存管理
实模式与保护模式:从逻辑地址到物理地址的完整寻址链路
按 空格/→ 演示下一步
全部页面点击任意一页,跳回舞台从这页播放
80x86汇编内存管理
实模式与保护模式:从逻辑地址到物理地址的完整寻址链路
内存地址与数据
CPU 干活时,指令和数据都躺在内存里。但 CPU 怎么精确定位某个字节?一次又能搬回多少字节?这就要靠三组总线协作。
门牌号位数定房间总数=寻址空间;包裹大小定一次能送多少=传输位宽
内存寻址模式
上页我们看到 CPU 拿到的是线性地址,但汇编指令要操作数据,得先说清「去哪儿找」。这一步就是寻址——它把指令里的字段翻译成真正要访问的位置。
立即数=自带货;寄存器=A号格;直接=柜号0123;间接=凭码找柜;基址变址=B区SI-08号
CPU与内存的数据流
三条总线各管一件事:地址管去向,控制管动作,数据管内容。
变量的声明语法
前面我们看到内存像一排编号货架,现在要把数据真正放上货架——汇编用三类伪指令声明变量,它们在编译时就决定了每个变量占几个格子。
小格放小件、大格放大件,格子尺寸在声明时就定死,标签名就是变量名
声明各种类型变量
用 MASM 语法展示 BYTE/WORD/DWORD 三种类型变量的声明与取值范围。
DB/DW/DD 后缀直接决定存储宽度(1/2/4 字节),h 后缀只是值的另一种书写方式,内存中的比特完全相同。
数据的存储方式
上页声明32位变量赋了 0x12345678 这种值,我们当一个整体处理。但内存按字节存取,这个32位数拆成四字节后,按什么顺序塞进连续地址?x86 的选择和大多数人直觉相反。
写数字时最左是最高位1;存字节时最左(低地址)是最低位78——同样自左向右走,权重方向相反
小端序 vs 大端序
对比字节在内存中的排列顺序
直接寻址
前面我们梳理了内存寻址的多种模式,现在挑最直白的一种——直接寻址:把内存地址直接当立即数写在指令里。CPU 看到它,不用拐弯,直接去那个地址取数。
门牌号就是指令里的偏移量,CPU 不绕弯,直接到该地址取数据
寄存器间接寻址
直接寻址把地址写死在指令里,间接寻址则把地址放进寄存器——相当于「门牌号随身带」,想改就改。
房卡=寄存器,存着房间号;刷卡进门=[],按卡内号码找房间
基址变址寻址
上页用一个寄存器装地址,就像只知道行号;再加一个寄存器就同时拿到行列号——这就是基址变址寻址:两个寄存器联手定位内存单元。
BX是排号,SI是座号;两数相加才得到具体座位
寻址方式代码对比
4 条 mov ax 看似不同,其实都从同一个内存单元取数;区别只在地址怎么算出来。
4 条 mov ax 都从 arr+4 取 30h,区别仅在偏移 4 的来源:硬编码、由 BX 提供、BX+SI 相加、BP+位移在栈帧里取。运行时地址需要变化就选②或③。
堆栈的基本机制
想象自助餐厅的弹簧盘架:每次把新盘子压到最上面,弹簧就会被压缩、所有旧盘子向下退一格——CPU 堆栈也是这个『向下退』的逻辑。
压盘子=把数据写入栈;弹簧收缩=SP 减 2;盘架最上层永远对应 SP 当前指向的栈顶
堆栈的增长方向
纵轴是地址高低,虚线箭头表示 PUSH 一次后 ESP 减 4,栈顶向低地址下移。
PUSH/POP指令对
用PUSH把两个值压栈,再用POP按相反顺序取出,无需临时寄存器即可完成交换。
栈是LIFO(后进先出)结构,所以PUSH顺序与POP目标必须"错开":先压AX再压BX,弹出时栈顶的BX先出,正好填入AX,AX再出来填入BX。
子程序调用与返回
前面学过PUSH/POP可以往堆栈存取数据。但你有没有想过:调用子程序时,CPU是怎么记住「等下要回到哪一行」的?答案就藏在CALL和RET这一对搭档里。
CALL=夹书签(压返回地址)+翻到目标页;RET=取回书签(弹出地址)+跳回继续读
调用子程序时堆栈变化
CALL时栈状态完整时序图
段寄存器与内存分段
上一页堆栈能自动存取,是因为有专门的段寄存器在管。80x86 共有 4 个段寄存器,每个对应一片内存区域、各管一摊事。
CS=生产线(CPU 只从这里取指令);DS=原料库;SS=中转台;ES=备用通道
逻辑地址到物理地址
从逻辑地址「段:偏移」出发,经两次变换得到20位物理地址。
内存复制与串操作
Linux x86-64 下用 REP 串指令完成复制、填充、逐字节读取,并验证输出。
CLD 令地址递增;MOVSB 从 RSI 读、写入 RDI,STOSB 将 AL 写入 RDI,LODSB 读入 AL。REP 由 ECX 决定次数,相关指针随后递增。
内存管理自测
在 8086 实模式下,DS=0x1000,CS=0x2000,指令 MOV AX,[BX] 中 BX=0x0050。该指令访问的物理地址是?
课后思考
先自己想,再点开参考答案——问题没有标准答案,只有思考路径。
参考答案8086数据线16位,纯偏移寻址只能覆盖64KB;引入段寄存器把地址空间扩到1MB。分段既是被硬件逼出来的解法,也成了后来保护模式的雏形。
参考答案RET仍能正确弹出返回地址,程序不会立刻崩溃。但堆栈会不断"长高",反复调用后耗尽栈空间,触发不可预测的崩溃——这是汇编里的"内存泄漏"。
参考答案MOV读写多字节数据时字节序翻转,PUSH/POP对字/双字的组装也需调整。但ADD、SUB这类纯算术指令只关心数值,与字节序无关,保持不变。