在 Linux 内核内存管理中,内存区域(Zone) 是对物理内存的逻辑划分,目的是适配不同硬件架构的内存特性、满足内核不同场景的分配需求。内核根据内存寻址能力 、硬件限制 和用途将物理页框划分到不同 Zone,每个 Zone 有独立的页分配器和管理策略。
核心设计目标
- 解决硬件限制 :例如 32 位系统中,直接映射的物理内存(
ZONE_DMA/ZONE_NORMAL)和高位内存(ZONE_HIGHMEM)的寻址差异。 - 区分内存用途:不同 Zone 对应不同的分配场景(如 DMA 设备专用内存、内核常规内存)。
- 简化内存管理:每个 Zone 独立维护空闲页链表、水位线(watermark),避免跨 Zone 竞争。
主流架构的 Zone 划分
不同 CPU 架构(32 位 x86、64 位 x86_64、ARM 等)的 Zone 划分存在差异,以下是最典型的 x86 架构 划分:
| Zone 类型 | 物理内存范围(x86 32 位) | 核心用途 | 关键特性 |
|---|---|---|---|
| ZONE_DMA | 0 ~ 16MB | 供 DMA 设备使用 | 1. 设备直接访问物理内存,无需 MMU 映射2. 地址必须在 16MB 以下(受老式 DMA 控制器限制)3. 内核直接映射到虚拟地址空间 |
| ZONE_NORMAL | 16MB ~ 896MB | 内核常规使用 | 1. 物理内存与内核虚拟地址一一直接映射 (线性映射)2. 内核访问无开销,是最常用的 Zone3. 虚拟地址 = 物理地址 + PAGE_OFFSET(x86 中为 0xC0000000) |
| ZONE_HIGHMEM | 896MB ~ 最大物理内存 | 高位内存 | 1. 超过 896MB 的物理内存,无固定虚拟地址映射 2. 内核需通过临时映射 (如 kmap())或永久映射 (如 vmalloc())访问3. 主要用于用户态进程,内核仅在必要时映射使用 |
在 64 位系统中,虚拟地址空间极大(如 48 位虚拟地址支持 256TB 空间),内核可以直接映射全部物理内存,因此 ZONE_HIGHMEM 几乎被废弃 ,仅保留 ZONE_DMA、ZONE_DMA32 和 ZONE_NORMAL:
- ZONE_DMA32:新增 Zone,供 32 位 DMA 设备使用,地址范围 0 ~ 4GB。
- ZONE_NORMAL:覆盖 4GB 以上的所有物理内存,全部直接映射。
注意 ZONE_HIGHMEM 的差异:64 位系统中无需处理临时映射,而 32 位系统中访问 ZONE_HIGHMEM 需使用 kmap()/kunmap()。
ZONE_HIGHMEM
ZONE_HIGHMEM 是 Linux 内核内存管理中专门用于标识高位内存 的内存区域,其核心特点是无固定内核虚拟地址映射,仅在 32 位架构下有实际意义,64 位架构中基本被废弃。
在 32 位 x86 架构 中,虚拟地址空间为 4GB ,内核默认划分 1GB 内核空间 (0xC0000000 ~ 0xFFFFFFFF)和 3GB 用户空间 (0x00000000 ~ 0xBFFFFFFF)。
- 内核空间需要映射物理内存以直接访问,1GB 内核虚拟地址最多只能映射 896MB 物理内存(剩余 128MB 用于内核自身镜像、vmalloc 区域等)。
- 当物理内存超过 896MB 时,超出部分无法被内核直接映射,这部分内存就被划分为 ZONE_HIGHMEM。
简单来说:ZONE_HIGHMEM 是 32 位系统突破 896MB 物理内存限制的关键机制。
核心特性
| 特性 | 说明 |
|---|---|
| 地址映射 | 无固定内核虚拟地址映射,需通过临时映射 或永久映射访问 |
| 物理范围 | 32 位 x86:896MB ~ 最大物理内存 |
| 主要用途 | 分配给用户态进程(用户页表可自由映射),内核仅在必要时临时映射使用 |
| 分配限制 | 内核无法直接分配 ZONE_HIGHMEM 内存作为内核数据结构(如内核栈、页表),因为这些结构需要永久映射 |
| 架构差异 | 64 位 x86_64 架构虚拟地址空间极大(如 48 位虚拟地址支持 256TB),内核可直接映射全部物理内存,因此 ZONE_HIGHMEM 无意义,内核配置中默认关闭 |
ZONE_HIGHMEM的两种访问方式
由于 ZONE_HIGHMEM 没有固定虚拟地址,内核访问其物理页时必须通过动态映射的方式;
1. 临时映射(kmap/kunmap)
-
原理 :内核预留一块固定大小的虚拟地址区域 (称为
kmap_atomic区域),作为临时映射窗口,每次映射一个物理页,用完立即释放。 -
特点 :原子操作,不可睡眠,适用于中断上下文或持有自旋锁的场景。
-
核心接口
// 临时映射物理页到内核虚拟地址,返回虚拟地址 void *kmap_atomic(struct page *page); // 解除临时映射 void kunmap_atomic(void *kvaddr); -
限制:映射窗口数量有限(如 x86 为 4 个),不可嵌套调用。
2. 永久映射(kmap/kunmap)
-
原理 :使用
vmalloc区域的虚拟地址,为物理页建立长期映射,映射后可睡眠,适用于进程上下文。 -
核心接口
// 为物理页建立永久映射,若页在 ZONE_NORMAL 则直接返回虚拟地址 void *kmap(struct page *page); // 解除永久映射 void kunmap(struct page *page); -
特点:映射数量无严格限制,但需避免频繁映射 / 解除以减少开销。
在 32 位 x86 系统中,内核内存分配的 Zone 优先级从高到低为:ZONE_DMA > ZONE_NORMAL > ZONE_HIGHMEM
- 内核优先从高优先级 Zone 分配内存(如 DMA 设备必须用 ZONE_DMA)。
- 当目标 Zone 内存不足时,才会尝试从低优先级 Zone 分配(需满足
gfp_mask限制)。 - ZONE_HIGHMEM 是最后被尝试的 Zone,且仅能分配给用户态或允许临时映射的内核场景。
在 x86_64 等 64 位架构 中,虚拟地址空间足够大(如 48 位虚拟地址支持 256TB),内核可以直接映射全部物理内存,因此:
- 内核配置中
CONFIG_HIGHMEM默认关闭,ZONE_HIGHMEM 不存在。 - 所有物理内存被划分为
ZONE_DMA(0~16MB)、ZONE_DMA32(0~4GB)和ZONE_NORMAL(4GB 以上)。 - 无需使用
kmap等临时映射接口,内核可直接通过page_to_virt()获取物理页的虚拟地址。
Zone 的核心数据结构

Zone 的关键管理机制
-
伙伴系统(Buddy System): 每个 Zone 独立维护伙伴系统,用于管理连续物理页的分配与释放。
free_area数组按页块大小(2^0~2^MAX_ORDER-1个页)组织空闲页,解决内存碎片问题。 -
**水位线(Watermark):**每个 Zone 有三个水位,触发不同的内存管理行为:
- WMARK_MIN:最低水位,内存极度紧张,禁止普通分配,仅允许内核紧急分配。
- WMARK_LOW:低水位,触发内存回收(kswapd 内核线程)。
- WMARK_HIGH:高水位,内存充足,kswapd 停止回收。
-
跨 Zone 分配策略: 当目标 Zone 内存不足时,内核会尝试从优先级更低 的 Zone 分配(需配置
CONFIG_ZONE_DMA等选项),优先级顺序:-
ZONE_DMA<ZONE_NORMAL<ZONE_HIGHMEM(32 位) -
ZONE_DMA<ZONE_DMA32<ZONE_NORMAL(64 位)
-
特殊 Zone 类型
ZONE_DEVICE
ZONE_DEVICE 是 Linux 内核 4.0+ 引入的特殊内存区域,用于管理非系统 RAM 的设备内存(如 PMEM、GPU 显存、NVDIMM 等),这类内存不属于系统物理内存,通过 device memory 机制接入内核内存管理体系;
核心价值是通过内核统一的页管理框架(struct page)为设备内存提供映射、分配和生命周期管理能力,同时支持热插拔与非易失特性。
| 核心定位 | 说明 |
|---|---|
| 非 RAM 内存的抽象 | 管理物理上不属于系统主存的设备内存,如持久内存、加速器显存 |
| 统一页管理 | 为设备内存创建 struct page 实例,使其可被伙伴系统、slab 等内核子系统识别 |
| 热插拔兼容 | 支持设备内存的动态添加 / 移除,不依赖系统启动时的内存初始化流程 |
| 映射与访问 | 提供内核 / 用户态映射接口,支持设备内存的直接访问或 DMA 操作 |
| 架构无关 | 适配 x86_64、ARM64 等所有主流架构,仅需启用 CONFIG_ZONE_DEVICE 配置 |
核心差异
- 物理本质不同:传统 Zone 管理系统 RAM,ZONE_DEVICE 管理设备提供的内存(非 RAM),断电后数据可能持久化(如 PMEM)。
- 页分配行为 :
- 传统 Zone 由内核伙伴系统负责分配 / 释放,支持
alloc_pages()等通用接口。 - ZONE_DEVICE 的内存由设备驱动或内存控制器(如 libnvdimm)管理,内核仅创建
struct page并维护映射关系,不参与物理页的分配。
- 传统 Zone 由内核伙伴系统负责分配 / 释放,支持
- 生命周期独立:设备内存的生命周期与设备绑定,支持热插拔(如 NVDIMM 动态上线 / 下线),传统 Zone 则依赖系统启动时的内存初始化。
- 内存回收策略:ZONE_DEVICE 不参与内核常规内存回收(如 kswapd 扫描),其回收由设备驱动或用户态程序主动触发。
核心数据结构

ZONE_DEVICE 的 struct page 包含专属字段,用于关联设备信息:

核心操作流程
分配 dev_pagemap:初始化设备指针、物理地址范围、分配 / 释放回调。
-
设备内存注册
-
分配
dev_pagemap:初始化设备指针、物理地址范围、分配 / 释放回调。 -
创建
struct page数组 :通过memremap_pages()为设备内存创建页描述符,关联到 ZONE_DEVICE。 -
注册到内存节点 :ZONE_DEVICE 被关联到特定 NUMA 节点,内核通过
node_zones管理。 -
内存映射与访问
- 内核态映射 :使用
devm_memremap()将设备物理地址映射到内核虚拟地址,适用于驱动直接访问。 - 用户态映射 :通过
mmap()系统调用,将 ZONE_DEVICE 管理的设备内存映射到用户空间(需驱动实现mmap接口)。
- 设备内存注销
当设备移除时,驱动通过以下步骤清理:
- 调用
memunmap_pages(pgmap)释放struct page数组。 - 通过
devm_kfree()释放dev_pagemap结构。 - 内核自动从 ZONE_DEVICE 中移除该内存范围,避免悬空引用。
典型应用场景
- 持久内存(PMEM/NVDIMM) :通过
libnvdimm驱动将 NVDIMM 设备内存注册到 ZONE_DEVICE,支持mmap持久化访问,用于数据库、键值存储等场景。 - GPU / 加速器显存 :显卡驱动将显存注册为 ZONE_DEVICE,内核通过
struct page管理显存页,支持 DMA 操作和用户态直接映射(如 CUDA 显存映射)。 - 热插拔设备内存:工业场景中动态添加 / 移除的内存扩展卡,通过 ZONE_DEVICE 实现无重启内存扩容。
ZONE_MOVABLE
ZONE_MOVABLE 是 Linux 内核中核心的「内存碎片治理专用」内存域 ,内核 3.8+ 正式引入并完善,是解决物理内存碎片化 的核心方案,通过 CONFIG_MOVABLE_NODE 启用。
ZONE_MOVABLE(可移动内存域)是 Linux 内核划分的标准内存区域 ,属于物理内存(系统 RAM)的一部分,专门存放「可迁移的物理内存页」 ,内核通过该区域将「可移动页」和「不可移动页」做强制隔离,彻底根治长期运行系统的内存碎片问题。
内存碎片
Linux 内核的伙伴系统(buddy)能解决小块内存碎片 ,但解决不了长期运行产生的「顽固内存碎片」:
内核中存在两种类型的物理页:
- 不可移动页 (Unmovable Pages) :页的物理地址绝对不能变,一旦分配后物理地址固定。比如:内核栈、内核代码段、内核数据结构、DMA 缓冲区、页表本身、设备驱动的核心缓冲区。
- 可移动页 (Movable Pages) :页的物理地址可以随意迁移,内核提供成熟的页面迁移机制(page migration),迁移后虚拟地址不变,仅修改物理地址映射。比如:用户态进程的匿名页、文件缓存页、共享内存页。
没有 ZONE_MOVABLE 时,可移动页和不可移动页会随机混杂在 ZONE_NORMAL 中 ,长期分配 / 释放后,内存中会充斥着大量「被不可移动页隔开的小空闲块」,内核无法凑出连续的大物理页 (比如 2MB/1GB 巨页),哪怕总空闲内存很多,也会出现「内存充足但大页分配失败」的问题,这就是内存碎片化。
ZONE_MOVABLE 的核心目标 :把所有「可移动页」塞进这个专属区域,把「不可移动页」留在 ZONE_DMA/ZONE_DMA32/ZONE_NORMAL 这些普通 Zone,物理地址上彻底隔离两类页面,让普通 Zone 不会产生碎片,让 ZONE_MOVABLE 内部的碎片可以通过「页面迁移」轻松合并成连续大页。
核心特性
- 仅存放「可移动页」:ZONE_MOVABLE 是「纯可移动页容器」,内核严格禁止任何「不可移动页」被分配到该区域,这是硬性规则;
- 页面可被内核主动迁移 :内核的
migrate_pages()接口可以随时把 ZONE_MOVABLE 内的物理页迁移到同 Zone 的其他物理地址,无任何副作用; - 属于「系统物理内存」 :和
ZONE_DEVICE完全不同,ZONE_MOVABLE 管理的是真实的内存条 RAM,不是设备内存,断电数据丢失; - 无固定物理地址范围 :和 ZONE_DMA (0~16MB)、ZONE_NORMAL (16~896MB) 不同,ZONE_MOVABLE 没有固定的物理内存区间,它是从 ZONE_NORMAL 中「划出一部分内存」形成的逻辑分区;
- 不参与 DMA 分配:DMA 设备需要的内存是「不可移动 + 物理地址连续 + 固定范围」,因此 ZONE_MOVABLE 永远不会被用于 DMA 分配;
- 无独立伙伴系统:ZONE_MOVABLE 复用内核的伙伴系统管理空闲页,和普通 Zone 一致,支持页块的合并 / 拆分。
- ZONE_MOVABLE 是唯一「纯可移动页」的标准物理内存域,这是它和所有其他 Zone 的本质区别。