深入理解 Linux 内存管理机制:从用户空间到内核空间的全景视角


支持作者新书,点击京东购买《Yocto项目实战教程:高效定制嵌入式Linux系统》


B站讲解视频


Linux 内核的内存管理子系统是操作系统中最复杂、最核心的组成部分之一。无论是用户程序运行的虚拟地址空间,还是内核中各种驱动、数据结构的内存分配,其底层都依赖于精巧设计的内存管理机制。本文将围绕用户空间与内核空间的差异、核心概念、关键机制与典型实战问题,逐步构建起清晰完整的知识图谱。


一、用户空间与内核空间的内存区别

Linux 将整个虚拟地址空间划分为两个区域:

维度用户空间内核空间
地址范围通常 0x00000000 ~ 0xBFFFFFFF(32位)通常 0xC0000000 ~ 0xFFFFFFFF
访问权限仅用户态进程可访问仅内核态代码可访问
分配方式malloc(), mmap() 等kmalloc(), vmalloc(), alloc_pages() 等
分配器用户态使用 glibc 封装(底层调用 sys_brk/sys_mmap)使用内核页帧分配器、slab/slub 等
是否可以直接访问物理地址❌ 不可✅ 通过页表间接访问

二、核心内存管理机制概览

Linux 内核内存管理由多个机制协同组成,每种机制负责不同粒度或阶段的内存操作。

1. 页帧管理机制(Page Frame Allocator)

  • 单位:以页为单位分配内存,页大小通常为 4KB(x86/ARM)。
  • 基本机制:伙伴系统(Buddy Allocator)
  • 管理结构:struct page

在这里插入图片描述

2. 内核小块内存管理:Slab/Slub

  • 适用于频繁申请释放的小对象,如 task_struct、inode、dentry。
  • 分配单位:object,而不是 page。
  • SLAB -> SLUB(现代默认)

3. memblock(启动早期临时管理器)

  • 只在启动阶段使用,未建立起完整页表前。
  • 后续交由 buddy 系统管理。

4. CMA(Contiguous Memory Allocator)

  • 作用:为 DMA 提供连续的物理内存。
  • 使用场景:GPU、ISP、音视频 DMA 需求。

5. vmalloc

  • 虚拟地址连续,但物理地址不一定连续。
  • 适合分配大块内存,但开销比 kmalloc 大。

6. percpu 分配器

  • 为每个 CPU 分配一份对象副本,避免锁争用。

三、页表结构解析与地址转换

在分页机制下,虚拟地址需经过页表转换为物理地址。

1. 多级页表结构(以 64 位架构为例):

VA (虚拟地址) ➔ PGD ➔ PUD ➔ PMD ➔ PTE ➔ 页框
缩写含义
PGDPage Global Directory
PUDPage Upper Directory
PMDPage Middle Directory
PTEPage Table Entry

每级结构索引不同位数,共同组成完整地址映射关系。

2. TLB(Translation Lookaside Buffer)

  • 硬件缓存页表项,加快地址转换。
  • 命中:无需访问内存
  • 未命中:需访问页表(页表项位于内存中)

四、物理内存架构模型

三种主流模型总结:

模型宏定义特点典型平台
Flat Memory ModelCONFIG_FLATMEM连续物理内存,无洞洞嵌入式 SoC、低端 ARM
Discontiguous ModelCONFIG_DISCONTIGMEM多 bank,存在间隙NUMA 早期 SMP 系统
Sparse Memory ModelCONFIG_SPARSEMEMSection 管理,支持热插拔大内存 NUMA 系统,x86_64

五、结构体关系解读

1. struct pglist_data(每个 NUMA 节点)

struct pglist_data {
    struct zone node_zones[MAX_NR_ZONES];
    ...
};

2. struct zone(内存管理区:DMA, Normal, HighMem)

struct zone {
    struct page *mem_map; // 页框描述符数组
    ...
};

3. struct page(物理页描述符)

struct page {
    unsigned long flags;
    struct address_space *mapping;
    void *virtual; // 页映射后的虚拟地址(有条件存在)
    ...
};

结构关系图:

[Node] struct pglist_data
   └─ zone[N]  // struct zone
         └─ page[M] // struct page

六、典型实战问题与解答

问题 1:用户程序频繁崩溃,是否与内存分配相关?

定位方法:

  • 使用 strace 查看是否 brk/mmap 系统调用失败
  • dmesg 查看是否存在 OOM 杀手信息

根因分析:

  • 用户空间 malloc 失败底层反映内核页分配失败(如内存碎片、zonelist 无法满足)

解决建议:

  • 优化分配粒度,复用内存
  • 调整内核参数或限制内存使用

问题 2:驱动中 kmalloc 分配失败如何排查?

观察点:

  • slabinfo (cat /proc/slabinfo)
  • 分配大小是否超过 kmalloc-512, kmalloc-1024 类缓存池

建议使用:

  • 过大则使用 vmalloc() 或 CMA

七、经典概念类问题总结

  1. 什么是 struct page?为什么不是每页都需要一个?

    • 用于记录每个页框的状态信息,如引用计数、所属 zone。
    • 某些内存区域(如设备保留内存)无需 struct page 管理。
  2. 什么是 memblock?

    • 内核早期内存管理器,在 MMU 启动前分配内存;之后由 buddy 管理接管。
  3. CMA 和 Buddy 的关系?

    • CMA 是 Buddy 的一个子区域,标记为“可回收但保留连续性”。
  4. vmalloc 与 kmalloc 的本质区别?

    • kmalloc 分配的是物理地址连续的内存。
    • vmalloc 虚拟地址连续,物理地址可能不连续。
  5. SLAB/SLUB 区别?

    • SLUB 是对 SLAB 的改进,结构更简洁,锁竞争更少,是当前主流选择。

八、总结:构建清晰的知识体系

通过上述内容,可以构建出 Linux 内存管理机制的知识地图:

          +---------------------------+
          | 用户态分配(malloc/mmap) |
          +-------------+-------------+
                        |
             +----------v----------+
             | 内核系统调用接口层   |
             +----------+----------+
                        |
        +---------------v----------------+
        |      页帧分配器(Buddy)        |
        |   + memblock/CMA/SLUB 等子模块 |
        +--------------------------------+
                        |
                +-------v--------+
                | 物理内存页表映射 |
                +----------------+

九、下一步学习建议

  • 阅读 mm/page_alloc.c 理解页面分配流程
  • 阅读 mm/slub.c 了解内核对象管理逻辑
  • 自行实现一个 kmalloc 模拟器或 slab 分配器
  • 实战调试:结合 ftrace + perf + kmemleak 检查内存行为

📌 博文结尾建议:

本文为 Linux 内核内存管理核心知识总结,涉及了从用户空间申请,到页表转换,再到内核多种分配器的使用场景与机制。如果你对性能优化、内存泄漏排查、驱动开发感兴趣,这些内容将是你不可缺的基础。


点击,进入B站讲解视频



支持作者新书,点击京东购买《Yocto项目实战教程:高效定制嵌入式Linux系统》


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值