计算机组成与体系结构:直接内存映射(Direct Memory Mapping)
目录
CPU地址怎么找到真实的数据?
内存映射的基本单位和结构
1. Pages(页)——虚拟地址空间的基本单位
2. Frames(页框)——物理内存空间的基本单位
3. Blocks(块)——主存和缓存之间的数据单位
4. Lines(行)——缓存中的数据单位
什么是 word(字)?
什么是 byte-addressable memory(按字节寻址)?
🧬什么是物理地址位?
📐 为什么我们要设计“物理地址位”?
🧮 示例讲解:直接内存映射过程
地址如何拆分?
为什么需要一种“映射规则”?
映射过程
地址拆解(Address Breakdown)
CPU地址怎么找到真实的数据?
当 CPU 执行一条指令,比如要访问变量 x
,它会给出一个地址。但这个地址是虚拟地址(Virtual Address),不是内存真实的物理地址。
这时就出现了一个问题:
💡 CPU 提供的是“虚拟地址”,但数据真实存在“物理内存”里。那怎么找得到?
这就需要一种转换机制,叫做——内存映射(Memory Mapping)
内存映射的基本单位和结构
内存映射是一种地址翻译机制,它负责把虚拟地址 → 物理地址,好比给你提供“地图导航”,告诉你数据在哪一层、哪一块。
为了实现这个映射,操作系统就把地址空间划分成若干个单位。
1. Pages(页)——虚拟地址空间的基本单位
问题:虚拟地址怎么组织的?
操作系统会把整个虚拟地址空间分成很多等大小的“小段”,每一段就叫做一个 Page(页)。
举例: 你写的程序其实用的是“虚拟地址”,操作系统会把这些地址一页一页地管理。
比如:每页大小是 4KB,你程序申请的内存就被切成一个个 4KB 的“页”。
2. Frames(页框)——物理内存空间的基本单位
问题:虚拟页放到内存的哪里?
页框是把物理内存(RAM)划分成的固定大小的“物理单位”,大小和“页”一样。
这样操作系统可以把某个虚拟页映射到物理内存中的某个页框中,建立虚实对应关系。
Page ↔ Frame 映射过程
每个虚拟页(page)被映射(mapping)到某个物理页框(frame)上。
虚拟内存通过 页表(Page Table) 建立从 Page → Frame 的映射关系。
这就是内存映射中最基本的核心:页式管理(Paging)。
但问题又来了:内存慢,CPU快,怎么办?
即使映射成功了,从内存里读数据还是太慢。怎么办呢?
我们再往内存映射系统的底层走,进入 缓存(Cache) 世界。这就引出了接下来的两个概念:
3. Blocks(块)——主存和缓存之间的数据单位
问题:主存数据怎么搬到 Cache?
主存(内存)被划分成很多块(blocks),是缓存系统从主存读取数据的最小单位。
举例: 如果一个 block 是 64 字节,那缓存每次从内存中读取数据,都是按块来取的,不是按字节。
4. Lines(行)——缓存中的数据单位
这些搬来的 block,会被临时存放在缓存中,对应存到缓存中的一个 Cache Line(行)。
-
Block 是从主存来的;
-
Line 是放在缓存里的;
-
两者一一对应。
概念 | 属于系统 | 对应关系 | 说明 |
---|---|---|---|
page | 虚拟内存 | → frame | 虚拟地址空间中的一页 |
frame | 物理内存 | ← page | 实体内存中的一块 |
block | 主存 | → line | 内存中被缓存系统按块划分 |
line | 缓存 | ← block | 缓存中存放主存数据的最小单元 |
什么是 word(字)?
在计算机中,word(字)是CPU一次能处理的数据宽度,是 CPU 使用的基本数据单位。
这个宽度取决于你的CPU是几位的:
CPU位数 | 一个 word 的大小 |
---|---|
16位 | 2 字节(16 bits) |
32位 | 4 字节(32 bits) |
64位 | 8 字节(64 bits) |
举个例子:
在 32 位 CPU 中,一个 word = 4 个字节。
如果你要访问一个整型变量(int),它通常就是一个 word 宽度的数据。
什么是 byte-addressable memory(按字节寻址)?
Byte-addressable memory 是指:内存中的每个地址代表一个字节(8 bits)。
也就是说:
-
地址
0x0001
表示的是第 1 个字节; -
地址
0x0002
表示的是第 2 个字节; -
……以此类推。
这是现代计算机的标准做法。
举个例子:
假设内存中有数据:[0xFF, 0xAB, 0x01, 0x23]
那么这些字节分别位于:
-
地址
0x1000
:0xFF -
地址
0x1001
:0xAB -
地址
0x1002
:0x01 -
地址
0x1003
:0x23
👉 每个地址定位的是一个字节,这就叫“按字节寻址”。
类比:
-
byte 是每页纸上的“一个字”;
-
word 是每次你读的一整行;
-
byte-addressable 是你可以随便指出“某一个字”,非常灵活;
-
而 CPU 每次读的是一整行(word),更高效。
🧬什么是物理地址位?
物理地址位(Physical Address Bits)指的是用于唯一标识主存中每一个字节的二进制位序列。
✅ 为什么叫“物理”地址?
在计算机体系结构中,内存分为物理地址空间(Physical Address Space)和虚拟地址空间(Virtual Address Space):
-
虚拟地址是程序看到的地址
-
物理地址是实际在内存条上存放数据的位置
所以我们说的 Physical Address Bits 是:CPU 最终用来从主存中“定位数据”的真实地址位序列。
📐 为什么我们要设计“物理地址位”?
从第一性原理来看,我们提出一个核心问题:
主存很大,如何精确、高效地寻址?
用二进制位来编码地址。每一个物理地址都对应一个内存单元(通常是一个字节),并用一串 0/1 序列(即“物理地址位”)来表示它的位置。
如果主存大小是 NNN 个地址单元(比如字节),我们至少需要多少位(bits)来表示这些地址?
💡这就是 log₂(N) 的定义!
log₂(N) 是“用二进制(base 2)数表示 N 个不同值,至少需要多少位”。
比如:
-
log₂(64)=6 ⇒ 用 6 位可以表示 64 个不同地址(从 000000 到 111111)
-
log₂(16)=4 ⇒ 用 4 位可以表示 16 个不同块
🧮 示例讲解:直接内存映射过程
我们有一个主存(Physical Address Space),图中一共显示了64个字节(编号0~63),而这些地址被分成了16个块(blocks)。这些块是怎么映射到 Cache 中的呢?这就是“直接内存映射”的任务。
系统参数如下:
项目 | 参数 | 中文解释 |
---|---|---|
主存大小 | 64 个字节 | Physical Address Space 是 64B(字节寻址) |
每块大小 | 4 个字节(一个block有4个字) | 每个 Block 有 4 个连续地址 |
总块数 | 16 块 | 64 ÷ 4 = 16 blocks(编号 0 到 15) |
块编号所需位数 | log₂(16) = 4 bits | 表示 block 编号需要 4 位 |
每块中地址的偏移量 | log₂(4) = 2 bits | block 内地址偏移(Offset)需要 2 |
地址如何拆分?
图中展示了 6 位物理地址被分成两部分:
地址段 | 位数 | 中文含义 | 功能 |
---|---|---|---|
高位(左侧) | 4 位 | Block号 | 哪个数据块(block) |
低位(右侧) | 2 位 | 偏移量(offset) | 块内第几个字节(0~3) |
📌 举个实际地址的例子:
-
块号(Block Number):前4位
0111
= 7 ⇒ 说明这个地址在 第7个块 -
偏移(Offset):后2位
11
= 3 ⇒ 在第7块的 第3个字节
所以,物理地址31 指向 Block 7 的最后一个字节(即地址 31)
为什么需要一种“映射规则”?
我们假设:
-
Cache 大小:16字(words)
-
每个 Cache Line 大小:4字
-
主存:我们默认主存比 cache 大得多,比如 64字(也就是前面图片中的 16个 block,每个 block 是 4字)
我们现在的问题是:
主存中的块(block)不能同时分配给cache中的所有行(line),我们该怎么分配这些块?
于是我们需要一个规则
ROUND-ROBIN 式的映射 —— Direct Mapping(直接映射)
直接映射(Direct Mapping):让每个块只能去 Cache 中固定的一行。
这个过程类似一种 “轮流排班机制(Round-Robin)”。
映射过程
主存中的每一个块(Block)编号记作 i
Cache 中的行号记作 j
我们使用公式:
j = i mod Cache 行数
这就把主存中的 16 个块,轮流“安排”到 4 个 Cache 行中
地址拆解(Address Breakdown)
物理地址总共需要 6 位,因为主存有 64 个字节。
我们把这 6 位地址拆分成 3 个字段:
部分 | 中文解释 | 位数 | 功能 |
---|---|---|---|
Tag bits | 标记位 | 高位 | 区分映射到同一行的不同块 |
Line number | 行号/索引位 | 中间位 | 决定映射到 Cache 的哪一行 |
Offset | 块内偏移 | 低位(2位) | 定位 block 内具体的字节 |
图中的例子说明:
虽然 Block 3、7、11、15 是不同的内存块,但它们 都被映射到 Cache 的第3行,只能互相“打架”。
而这个打架的关键区分方式就是 Tag!