操作系统-内存寻址
内存寻址
- 一、地址概念
- 二、完整的转换过程(以 x86 保护模式为例)
- 举个例子
- 三、总结流程
- 四、现代CPU的变化趋势
- 五、总结一句话
- 六、简短结论先说
- 七、详细解释:实模式与保护模式的区别
- 八、为什么“实模式”看起来像“内核态”
- 九、实模式在现代系统中的位置
- 十、总结一句话:
- 十一、总体结论先说
- 十二、保护模式与长模式详细对比解释
- 1️⃣ 分段机制的区别
- 🔸 保护模式(32位)
- 🔸 长模式(64位)
- 2️⃣ 分页机制的区别
- 🔸 保护模式
- 🔸 长模式
- 3️⃣ 地址空间与虚拟地址宽度
- 4️⃣ 权限与特权级
- 5️⃣ 地址转换流程总结
- 🧩 保护模式(32位)
- 🧩 长模式(64位)
- 十四、总体对比图概念总结
- 十五 一句话总结:
CPU 地址变换的本质一般来说确实是这样:
段内偏移(offset) → 虚拟地址(virtual address) → 物理地址(physical address)
但要注意:这取决于 CPU 的体系结构(主要是 x86)和工作模式(实模式 / 保护模式 / 长模式)。
一、地址概念
名称 | 由谁负责 | 含义 |
---|---|---|
逻辑地址(Logical Address) | 程序员 / 编译器 / CPU段机制 | 由**段选择子 + 段内偏移(Offset)**组成 |
线性地址(Linear Address)或虚拟地址(Virtual Address) | 由 段机制(Segmentation) 转换得到 | 即“虚拟地址”,由 MMU 负责进一步转换 |
物理地址(Physical Address) | 由 MMU(分页机制) 转换得到 | 最终用于访问物理内存(DRAM) |
二、完整的转换过程(以 x86 保护模式为例)
CPU 访问内存时,通常经过以下三步:
1️⃣ 段内偏移 → 线性地址(虚拟地址)
由 段寄存器(CS、DS、SS、ES 等) + 段描述符表(GDT/LDT) 完成。
逻辑地址 = 段选择子:偏移量
段描述符给出:段基址(Base) + 段限长(Limit)
线性地址 = 段基址 + 段内偏移
2️⃣ 线性地址(虚拟地址) → 物理地址
由 MMU 的分页机制 完成。
页表将线性地址按页(4KB等)映射到物理页。
虚拟页号 → 页表查找 → 物理页号
物理地址 = 物理页基址 + 页内偏移
3️⃣ 物理地址 → 实际内存访问
CPU 最终使用这个物理地址访问内存总线。
举个例子
假设:
段寄存器 DS → 段基址 = 0x00400000
偏移量 offset = 0x00001000
第一步(分段转换):
线性地址 = 段基址 + 偏移量 = 0x00401000
第二步(分页转换):
查页表发现:
虚拟页 0x00401000 → 物理页 0x001AB000
得到:
物理地址 = 0x001AB000 + 页内偏移(0x000)= 0x001AB000
三、总结流程
阶段 | 转换单位 | 机制 | 谁参与 |
---|---|---|---|
段 → 线性地址 | 段寄存器 + 段基址 | 分段机制(Segmentation) | CPU段部件 |
线性地址 → 物理地址 | 页表 + TLB | 分页机制(Paging) / MMU | MMU |
最终访问物理内存。
四、现代CPU的变化趋势
现代 64 位系统(例如 x86-64 长模式、ARMv8 等):
-
分段机制几乎被废弃或简化
- 所有段基址几乎固定为 0
- 所以逻辑地址 ≈ 线性地址
-
只保留分页机制(MMU)来完成虚拟地址到物理地址的转换
也就是说:
在现代 CPU 中,地址转换路径几乎简化为:
虚拟地址 → 物理地址(仅分页机制)
段机制只在兼容旧模式或特权级切换时保留。
五、总结一句话
在有分段 + 分页机制的架构中(如 x86 保护模式):
段内偏移 → 虚拟地址(线性地址) → 物理地址
但在现代 CPU(如 x86-64、ARMv8)中,分段已被简化,
所以通常是:虚拟地址 → 物理地址
在现代操作系统中,程序中打印出的地址(例如通过 printf(“%p”))是 虚拟地址(Virtual Address),
不是段内偏移,也不是物理地址。
总结对比表
环境 | 打印的地址 | 是否经过 MMU | 备注 |
---|---|---|---|
现代操作系统(Linux/Windows) | ✅ 虚拟地址 | ✅ 是 | 每个进程独立地址空间 |
x86 实模式(如 DOS) | 段内偏移 | ❌ 否 | 地址 = 段基址×16 + 偏移 |
嵌入式无 MMU 系统(如 Cortex-M) | 实际物理地址 | ❌ 否 | 无虚拟内存机制 |
x86-64 长模式 | ✅ 虚拟地址 | ✅ 是 | 分段机制基本禁用 |
非常好的问题 👍,你在问 “实模式是不是内核态使用的”,这涉及到 CPU 的运行模式与 保护机制(Privilege) 的区别。
我们来一步步说明:
六、简短结论先说
❌ 实模式(Real Mode)不是内核态。
因为实模式下根本就没有“用户态 / 内核态” 的概念。
在 实模式 中,CPU 没有权限级(Privilege Level)保护机制,
所有代码(无论是谁写的)都能直接访问 所有内存、I/O端口、硬件寄存器。
也就是说:
实模式下的程序权限相当于“超级管理员”,但并不是所谓的“内核态”,因为没有分层概念。
七、详细解释:实模式与保护模式的区别
对比项 | 实模式 (Real Mode) | 保护模式 (Protected Mode) |
---|---|---|
地址空间 | 20位地址,总线可寻址 1MB | 32位或更高地址,可寻址 4GB+ |
分段机制 | 基址 = 段寄存器 × 16 | 段基址来自 GDT/LDT 表项 |
MMU(分页) | ❌ 无 | ✅ 有(支持虚拟内存) |
特权级(Privilege) | ❌ 无用户态/内核态区分 | ✅ 有 4 级(常用 ring 0 和 ring 3) |
可访问范围 | 任意访问内存、I/O | 根据特权级受限 |
用途 | 启动、BIOS、引导加载 | 操作系统运行 |
八、为什么“实模式”看起来像“内核态”
因为:
-
实模式代码能做所有“内核才能做的事”,例如:
- 直接访问硬件端口(I/O)
- 修改中断向量表
- 操作整个内存空间
-
所以从“权限”角度看,它相当于“完全特权态(ring 0)”
但要注意:
这是因为 CPU 还没启用保护机制,不是因为它“处于内核态”。
换句话说:
实模式 = “无防护的上帝模式(All Access)”
保护模式 = “带有安全分层的操作模式(用户态 / 内核态)”
九、实模式在现代系统中的位置
现代 x86 系统启动过程:
1️⃣ 开机后 CPU 默认进入 实模式
- 运行 BIOS / UEFI 初始化
- 可访问 1MB 内存空间
2️⃣ 引导加载程序(Bootloader)运行
- 例如 GRUB、Windows Boot Manager
- 最终把 CPU 切换到 保护模式(或长模式)
3️⃣ 操作系统内核(Linux / Windows)启动
- 启用分页机制(MMU)
- 正式进入 内核态(ring 0)
- 创建用户态(ring 3)进程
十、总结一句话:
🔹 实模式没有“内核态”或“用户态”的概念;
🔹 它只是 CPU 启动后的最原始模式;
🔹 保护模式启用后,才有真正意义上的内核态(ring 0)和用户态(ring 3)。
CPU从实模式 → 保护模式 → 长模式
非常好的问题 👍,你已经问到 x86 架构最核心的内存寻址机制演化 了。
这其实是 CPU 从 32 位(保护模式) → 64 位(长模式) 的关键区别所在。
我们可以从地址寻址机制、分段机制、分页机制、权限机制等几个角度对比👇
十一、总体结论先说
特性 | 保护模式(Protected Mode, x86 32位) | 长模式(Long Mode, x86-64 64位) |
---|---|---|
寻址方式 | 32 位线性地址(最多 4 GB) | 64 位虚拟地址(目前有效 48/57 位) |
分段机制 | ✅ 有效,段基址+段限长都参与地址计算 | ⚙️ 基本废弃,仅保留 FS/GS 寄存器可用 |
分页机制 | 二级或三级页表(4KB页) | 四级或五级页表(PML4、5级分页) |
地址转换流程 | 逻辑地址 → 线性地址 → 物理地址 | (段机制几乎无效)虚拟地址 ≈ 线性地址 → 物理地址 |
权限机制 | ring 0~3 四级权限 | 仍保留 ring 0~3,但常用 ring 0 和 ring 3 |
可寻址空间 | 最大 4 GB | 理论 2⁶⁴(当前实现 256 TB 虚拟空间) |
TLB/页表结构 | 32位页表(Page Directory / Page Table) | 64位页表(PML4 / PDP / PD / PT) |
十二、保护模式与长模式详细对比解释
1️⃣ 分段机制的区别
🔸 保护模式(32位)
-
有真正的 分段机制:
-
每个段寄存器(CS/DS/SS/ES/FS/GS)都有一个 段描述符
-
段描述符里有:
- Base(段基址)
- Limit(段长度)
- Privilege Level(特权级)
-
-
逻辑地址 = 段选择子 + 段内偏移
→ 线性地址 = Base + Offset
🧩 举例:
CS:0x2000
Offset: 0x1234
线性地址 = 0x2000 + 0x1234 = 0x3234
🔸 长模式(64位)
- 分段机制被废弃(除了 FS、GS)
- 所有段寄存器(CS、DS、ES、SS)的 Base 都强制为 0,Limit 也忽略。
- FS/GS 例外:保留用于线程局部存储(TLS)等用途(例如 Linux 的
gs:0x0
指向当前线程结构)。
✅ 所以在 64 位长模式下:
逻辑地址 ≈ 线性地址(因为段基址为 0)
几乎不再经过分段变换。
2️⃣ 分页机制的区别
🔸 保护模式
-
支持 2 级或 3 级页表:
- 页目录(Page Directory)
- 页表(Page Table)
-
每级 1024 项,每页 4KB
-
32 位地址被拆成:
[10位页目录] [10位页表] [12位页内偏移]
🔸 长模式
-
启用 四级分页(PML4),64 位页表结构:
PML4 → PDPT → Page Directory → Page Table → Page Frame
-
每级索引 9 位(共 36 位),加上页内偏移 12 位:
[9][9][9][9][12] = 48 位虚拟地址
-
支持大页(2MB、1GB 页)
⚙️ 如果启用 5-level paging(现代 CPU 可选),则地址空间扩展为 57 位虚拟地址(128 PB)。
3️⃣ 地址空间与虚拟地址宽度
模式 | 位宽 | 有效虚拟地址位 | 虚拟地址空间 |
---|---|---|---|
保护模式 | 32 位 | 32 位 | 4 GB |
长模式(4-level) | 64 位 | 48 位 | 256 TB |
长模式(5-level) | 64 位 | 57 位 | 128 PB |
⚠️ 高 16 位必须是“符号扩展位”,即第 47 位之后要与第 47 位相同(规范要求)。
4️⃣ 权限与特权级
两者都支持 ring 0~3 特权级:
- ring 0 → 内核态
- ring 3 → 用户态
但长模式去掉了 LDT 机制(每进程局部分段),简化为统一分页保护。
5️⃣ 地址转换流程总结
🧩 保护模式(32位)
逻辑地址(段选择子:偏移)↓ [段表转换]
线性地址↓ [页表转换,MMU]
物理地址
🧩 长模式(64位)
虚拟地址(≈线性地址)↓ [四级页表映射,MMU]
物理地址
十四、总体对比图概念总结
项目 | 保护模式 | 长模式 |
---|---|---|
分段机制 | 有效,参与地址计算 | 几乎禁用 |
页表层数 | 2/3 级 | 4/5 级 |
地址宽度 | 32 位 | 48/57 位有效 |
虚拟内存 | 最大 4GB | 可达数百TB |
访问保护 | 段+页双层保护 | 仅页级保护 |
常用用途 | 32位操作系统(WinXP、Linux 2.6) | 现代64位系统(Win10+、Linux x86-64) |
十五 一句话总结:
在 保护模式 下,地址转换是“两步走”(分段 + 分页);
在 长模式 下,分段基本被废弃,只保留分页;
地址空间从 4GB 扩展到数百 TB,分页层次从 2/3 级变成 4/5 级。