linux 内存区域(Zone)

在 Linux 内核内存管理中,内存区域(Zone) 是对物理内存的逻辑划分,目的是适配不同硬件架构的内存特性、满足内核不同场景的分配需求。内核根据内存寻址能力硬件限制用途将物理页框划分到不同 Zone,每个 Zone 有独立的页分配器和管理策略。

核心设计目标

  1. 解决硬件限制 :例如 32 位系统中,直接映射的物理内存(ZONE_DMA/ZONE_NORMAL)和高位内存(ZONE_HIGHMEM)的寻址差异。
  2. 区分内存用途:不同 Zone 对应不同的分配场景(如 DMA 设备专用内存、内核常规内存)。
  3. 简化内存管理:每个 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_DMAZONE_DMA32ZONE_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),内核可以直接映射全部物理内存,因此:

  1. 内核配置中 CONFIG_HIGHMEM 默认关闭,ZONE_HIGHMEM 不存在。
  2. 所有物理内存被划分为 ZONE_DMA(0~16MB)、ZONE_DMA32(0~4GB)和 ZONE_NORMAL(4GB 以上)。
  3. 无需使用 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 配置

核心差异

  1. 物理本质不同:传统 Zone 管理系统 RAM,ZONE_DEVICE 管理设备提供的内存(非 RAM),断电后数据可能持久化(如 PMEM)。
  2. 页分配行为
    • 传统 Zone 由内核伙伴系统负责分配 / 释放,支持 alloc_pages() 等通用接口。
    • ZONE_DEVICE 的内存由设备驱动或内存控制器(如 libnvdimm)管理,内核仅创建 struct page 并维护映射关系,不参与物理页的分配。
  3. 生命周期独立:设备内存的生命周期与设备绑定,支持热插拔(如 NVDIMM 动态上线 / 下线),传统 Zone 则依赖系统启动时的内存初始化。
  4. 内存回收策略:ZONE_DEVICE 不参与内核常规内存回收(如 kswapd 扫描),其回收由设备驱动或用户态程序主动触发。

核心数据结构

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

核心操作流程

分配 dev_pagemap:初始化设备指针、物理地址范围、分配 / 释放回调。

  1. 设备内存注册

  2. 分配 dev_pagemap:初始化设备指针、物理地址范围、分配 / 释放回调。

  3. 创建 struct page 数组 :通过 memremap_pages() 为设备内存创建页描述符,关联到 ZONE_DEVICE。

  4. 注册到内存节点 :ZONE_DEVICE 被关联到特定 NUMA 节点,内核通过 node_zones 管理。

  5. 内存映射与访问

  • 内核态映射 :使用 devm_memremap() 将设备物理地址映射到内核虚拟地址,适用于驱动直接访问。
  • 用户态映射 :通过 mmap() 系统调用,将 ZONE_DEVICE 管理的设备内存映射到用户空间(需驱动实现 mmap 接口)。
  1. 设备内存注销

当设备移除时,驱动通过以下步骤清理:

  1. 调用 memunmap_pages(pgmap) 释放 struct page 数组。
  2. 通过 devm_kfree() 释放 dev_pagemap 结构。
  3. 内核自动从 ZONE_DEVICE 中移除该内存范围,避免悬空引用。

典型应用场景

  1. 持久内存(PMEM/NVDIMM) :通过 libnvdimm 驱动将 NVDIMM 设备内存注册到 ZONE_DEVICE,支持 mmap 持久化访问,用于数据库、键值存储等场景。
  2. GPU / 加速器显存 :显卡驱动将显存注册为 ZONE_DEVICE,内核通过 struct page 管理显存页,支持 DMA 操作和用户态直接映射(如 CUDA 显存映射)。
  3. 热插拔设备内存:工业场景中动态添加 / 移除的内存扩展卡,通过 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 的本质区别。
相关推荐
xiep14383335101 小时前
Ubuntu 24.04.3 LTS 搭建离线仓库安装docker-ce
linux·ubuntu·docker
代码游侠2 小时前
学习笔记——ARM Cortex-A 裸机开发实战指南
linux·运维·开发语言·前端·arm开发·笔记
Jay Chou why did2 小时前
uboot—1.概述
linux
纵有疾風起2 小时前
【Linux 系统开发】基础开发工具详解:软件包管理器、编辑器。编译器开发实战
linux·服务器·开发语言·经验分享·bash·shell
乌萨奇也要立志学C++2 小时前
【Linux】信号量 信号量详解与应用和基于环形队列实现单 / 多生产消费模型
linux·c++
DN金猿2 小时前
ubuntu在apt安装时出现的弹窗详解
linux·运维·ubuntu
山上三树2 小时前
Linux C多线程的**所有底层核心原理**
linux
大地的一角2 小时前
(Linux)进程间通信
linux·运维·服务器
weixin_462446232 小时前
【原创实践】在 CentOS 上安装 JupyterHub 并配置 R 语言支持 Kernel
linux·r语言·centos