8086 → x86-32
汇编进阶 · 内存概念衔接 · 2026
汇编进阶 · 交互式图解

从 8086 到 x86-32,
内存概念怎么变?

你已经掌握 8086 实模式:段:偏移、直接操作物理内存。这一页帮你跨过到 32 位保护模式的门槛——虚拟内存、分页、进程地址空间、内存映射。这正是读懂《DLL 与 EXE 的关系》所需的全部前置。

8086:段:偏移 → 物理地址 x86-32:虚拟地址 → 页表 → 物理地址
前置:8086 汇编目标:x86 32 位保护模式衔接:DLL 与 EXE
01
第 1 章 · SECTION 01

变了什么

8086 实模式 vs x86-32 保护模式
01 / 06

你在 8086 学的是实模式:程序直接跑在硬件上,独享 1MB 物理内存,想访问哪个地址就访问哪个。而 x86-32 引入了保护模式,带来一系列根本变化:

对比项8086 实模式(你已会)x86-32 保护模式(新)
位数 / 寄存器16 位 · AX BX SP IP32 位 · EAX EBX ESP EIP(AX 是 EAX 的低 16 位)
地址空间1MB(20 位地址)4GB(32 位地址)
寻址方式段:偏移 → 物理地址平坦模型 + 分页 → 虚拟地址
看到的内存直接使用物理内存,大家共用每个进程独立的虚拟内存
内存保护无,可碰任何内存 / 硬件有,特权级 + 进程隔离
典型环境DOS 单任务Windows / Linux 多任务
为什么要变?

因为计算机进入了多任务时代——一台机器要同时跑很多程序,就必须解决三件事:① 互相隔离(一个程序崩溃别拖累别人);② 物理内存不够时能"变出"内存来用;③ 防止越权(普通程序不许乱碰系统和他人的内存)。保护模式 + 虚拟内存 + 分页,就是为解决这三件事而生的。

02
第 2 章 · SECTION 02

分段 → 平坦

寻址方式怎么变了
02 / 06

8086 之所以用"段:偏移",是因为 16 位寄存器最大只能表示 64KB,凑不出 1MB,只好用"段 × 16 + 偏移"拼出 20 位地址。到了 32 位,一个偏移寄存器就能表示 4GB,这套"拼图"就没必要了。先动手回顾一下你已经会的:

8086 实模式 · 段:偏移 计算器
物理地址 = 段 × 16 + 偏移
0x
0x
0x1000 × 0x10 + 0x2000
= 0x12000
x86-32 · 平坦模型(基址 = 0)
平坦模型 FLAT MODEL
  • 段寄存器还在(CS / DS / SS…),但操作系统把它们的基址全设为 0、界限设为 4GB
  • 于是:线性地址 = 0 + 偏移 = 偏移本身
  • 分段"名存实亡",你只需面对一个连续、平坦的 32 位地址空间
  • 不用再算"段 × 16"了
一句话记住

8086 是"两个 16 位数拼出一个 20 位地址";x86-32 是"基址固定为 0,一个 32 位偏移就是地址"。你写的地址(如 0x00401000)直接就是线性地址——但它还不是物理地址,下一章揭晓。

03
第 3 章 · SECTION 03

虚拟内存与进程

每个进程一个“私有的 4GB”
03 / 06

8086 时代,所有程序和 DOS 共用同一块 1MB 物理内存,谁都能看谁都能改——所以一个程序写错地址,常常整机死机。x86-32 的做法是:给每个进程发一个独立的、私有的 4GB"虚拟地址空间",彼此隔离。

FIG. 3.1 — 同一虚拟地址,不同物理位置

看下图:进程 A 和进程 B 都使用虚拟地址 0x00400000,但经过各自的页表,映射到了物理内存的不同位置——所以它们互不影响:

进程 A 的虚拟空间
VA 0x00400000
物理内存 RAM(只有一份)
帧 0x00A00000 ← 属于 A
帧 0x01300000 ← 属于 B
进程 B 的虚拟空间
VA 0x00400000
关键点

你在 32 位程序里写下的地址(如 0x00400000)是虚拟地址不是物理地址。真正的物理位置由操作系统和 CPU 在幕后决定。这样,A 程序崩溃不会踩到 B,也不用担心地址撞车。

4GB 怎么分

每个进程的 4GB 虚拟空间里,低 2GB0x0 ~ 0x7FFFFFFF)给用户程序用,高 2GB0x80000000+)是内核区,用户程序碰不到——这就是"保护"。

04
第 4 章 · SECTION 04

分页机制

虚拟地址如何变成物理地址
04 / 06

既然程序用的是虚拟地址,CPU 怎么找到真正的物理内存?靠分页页表。这是从 8086"地址即物理"到 x86"地址要翻译"的最大转变。先建立三个概念:

分页 PAGING

把虚拟空间和物理内存都切成固定大小(4KB = 0x1000)的小块。虚拟的叫"页",物理的叫"页帧"。

页表 PAGE TABLE

每个进程一张"对照表",记录 虚拟页号 → 物理页帧(或"不在内存")。

地址拆分 VPN + OFFSET

虚拟地址 = [虚拟页号 | 页内偏移]。低 12 位是页内偏移,其余是页号。

按需调页 DEMAND PAGING

页表可标"该页不在内存"。访问到就触发缺页中断,OS 从磁盘把它读进 RAM 再继续。所以内存只装"正在用的页"。

FIG. 4.1 — 一次虚拟地址翻译(点“下一步”)

以虚拟地址 0x00401002 为例,看 CPU 和操作系统如何一步步把它翻译成物理地址:

虚拟页号(高 20 位)0x00401
页内偏移(低 12 位)0x002

和 8086 对比着记

; 8086 实模式:地址 = 物理地址,一步到位 物理地址 = 段 × 16 + 偏移 ; x86-32 保护模式:地址要“翻译”一次 物理地址 = 页表[虚拟页号](得物理页帧) + 页内偏移
记忆钩子

8086 是"查一次加法"(段×16+偏移);x86-32 是"查一次表"(页号→页帧)再加偏移。多出来的这张"页表",正是虚拟内存、进程隔离、按需调页的总开关。

05
第 5 章 · SECTION 05

内存映射

EXE / DLL 怎么“住”进虚拟内存
05 / 06

有了"虚拟内存 + 分页 + 按需调页",你就能理解 EXE 和 DLL 到底是怎么加载的了——靠的是内存映射文件

1建立映射把 EXE / DLL 文件映射到虚拟地址空间(如 0x00400000 / 0x7C800000),先不读内容
2访问某页程序执行到某个还没调入的代码页
3缺页中断CPU 发现该虚拟页"不在内存",通知操作系统
4从磁盘读入OS 把那一页从 EXE / DLL 文件读进 RAM,更新页表,继续执行
串起来

"EXE 加载到 0x00400000、DLL 加载到 0x7C800000",说的都是虚拟地址;它们的物理页是按需调入的,而且同一份系统 DLL 的物理页还能在多个进程间共享——省内存。

通往下一课

这正是《DLL 与 EXE 的关系》里"映射 DLL 到进程地址空间"的底层机制。概念已经补齐,可以去那里看 EXE 与 DLL 如何协同工作了。

学完自测 · QUIZ

自测一下

6 道题 · 点选项即看答案与解析

这 6 道题检验你是否完成了从 8086 到 x86-32 的概念跨越。 尚未作答

Q18086 实模式下,物理地址的计算方式是?
答案:段 × 16 + 偏移。段左移 4 位(×16)再加偏移,把两个 16 位数拼成 20 位物理地址。例如 0x1000:0x2000 → 0x12000。
Q2x86-32 保护模式下,程序里写的地址就是物理内存的真实地址。
答案:错。程序用的是虚拟地址,需经页表翻译才能得到物理地址。这正是与 8086 最大的不同。
Q332 位平坦模型下,分段"名存实亡",是因为?
答案:段基址被设为 0。段寄存器还在,但基址全为 0、界限为 4GB,所以"线性地址 = 偏移",你面对的就是一个连续平坦的 32 位空间。
Q4x86-32 中,一页(页 / 页帧)的通常大小是?
答案:4KB(0x1000)。所以虚拟地址的低 12 位是"页内偏移",其余高位是"虚拟页号"。
Q5每个进程都有自己独立的虚拟地址空间,彼此隔离。
答案:对。两个进程即使使用同一个虚拟地址,也会经各自页表映射到不同的物理帧,互不影响——这就是隔离与保护。
Q6程序访问一个"不在物理内存"的虚拟页时,会发生什么?
答案:触发缺页中断。操作系统把那一页从磁盘(或 EXE/DLL 文件)读进 RAM、更新页表,再重新执行该指令。这就是"按需调页",内存映射 EXE/DLL 也靠它。