Linux内核分析之内存管理-00
Linux 6.12 内存管理概览
基于 Linux 6.12.38 源码,涵盖物理内存与虚拟内存管理的核心机制
1. 概述
1.1 内存管理子系统职责
核心功能:
- 物理内存管理:页面的分配、释放、回收
- 虚拟内存管理:地址空间的创建、映射、保护
- 页表管理:多级页表的创建、更新、遍历
- 缺页处理:按需加载、写时复制
- 页面回收:LRU 算法、交换机制
- 内存共享:进程间共享内存、写时复制
1.2 内存管理架构
1 | ┌────────────────────────────────────────────────────────────────┐ |
1.3 关键源码位置
| 功能 | 目录 | 主要文件 |
|---|---|---|
| 核心内存管理 | mm/ |
memory.c, page_alloc.c |
| 页表管理 | mm/ |
pgtable.c, fault.c, mmap.c |
| Slab 分配器 | mm/ |
slab.c, slub.c, slob.c |
| 页面回收 | mm/ |
vmscan.c |
| 交换机制 | mm/ |
swap.c, swap_state.c, swapfile.c |
| 内存压缩 | mm/ |
compaction.c |
| 页缓存 | mm/ |
filemap.c, page-io.c |
| 匿名内存 | mm/ |
mprotect.c, mlock.c |
| 内存 cgroup | mm/ |
memcontrol.c |
| KSM | mm/ |
ksm.c |
| 页迁移 | mm/ |
migrate.c |
| 内核页表 | arch/*/mm/ |
pgtable.c, init.c |
| 头文件 | include/linux/ |
mm.h, mm_types.h, mmzone.h, pgtable.h |
| 体系相关 | arch/*/mm/ |
各种体系结构相关文件 |
1.4 相关数据结构概览
1 | struct page; // 物理页面描述符 |
2. 物理内存模型
2.1 内存节点 (Node)
NUMA 架构:
1 | // 节点定义 (include/linux/mmzone.h) |
节点状态:
1 | Node 0 (正常内存节点) |
2.2 内存区域 (Zone)
区域类型:
1 | enum zone_type { |
水位 (Watermark):
1 | │ |
2.3 物理页面 (Page)
位置: include/linux/mm_types.h
1 | struct page { |
页面标志:
1 | // 页面状态标志 |
2.4 复合页 (Compound Page)
结构:
1 | ┌────────────────────────────────────────────────────────────┐ |
Folios (多页复合页):
1 | // Linux 5.15+ 引入的 folio 结构 |
3. 物理内存分配器
3.1 Buddy System 概述
分配器原理:
- 将物理内存按 2^n 次幂大小组织
- 相邻大小的空闲块可以合并
- 分割大块满足小块分配请求
- 回收时合并相邻块
空闲区域数组:
1 | struct zone { |
Buddy System 结构:
1 | Order 0 (4KB) : [P] [P] [P] [P] [P] [P] [P] [P] ... |
3.2 分配与释放
分配页面:
1 | // 位置: mm/page_alloc.c |
分配流程:
1 | ┌────────────────────────────────────────────────────────────┐ |
释放页面:
1 | // 释放页面 |
释放流程:
1 | free_pages(page, order=1) |
3.3 GFP 标志
定义: include/linux/gfp.h
区域修饰符:
1 |
行为修饰符:
1 |
常用组合:
1 | // 可以睡眠和回收 |
3.4 Per-CPU 页面缓存
目的: 减少自旋锁竞争,提高分配性能
结构:
1 | // Per-CPU 页面缓存 |
分配流程 (带 PCP 缓存):
1 | alloc_pages(GFP_KERNEL, order=0) |
4. 虚拟地址空间
4.1 地址空间布局
x86_64 (48位虚拟地址):
1 | ┌─────────────────────────────────────────────────────────────────┐ |
ARM64 (48位/52位虚拟地址):
1 | 用户空间 (TTBR0_EL1): 0x0000000000000000 - 0x0000ffffffffffff |
4.2 内存描述符 mm_struct
位置: include/linux/mm_types.h
1 | struct mm_struct { |
4.3 内核地址空间
直接映射区:
1 | // 物理内存线性映射到内核空间 |
vmalloc 区:
1 | // 非连续内存映射 |
固定映射区:
1 | // 编译时固定但虚拟地址可配置 |
5. 页表管理
5.1 多级页表结构
x86_64 (4级页表):
1 | ┌─────────────────────────────────────────────────────────────────┐ |
ARM64 (4级/5级页表):
1 | // 48位 VA (4级) |
5.2 页表项格式
PTE 格式 (x86_64):
1 | ┌─────────────────────────────────────────────────────────────────┐ |
5.3 页表操作
创建页表:
1 | // 创建页表 |
查找页表项:
1 | // 查找 PTE |
设置页表项:
1 | // 设置 PTE |
5.4 TLB 管理
TLB 刷新:
1 | // 刷新单个地址 |
TLB 操作:
1 | ; x86_64 TLB 指令 |
5.5 巨页
普通巨页 (Huge Pages):
1 | // 2MB 巨页 (x86_64) |
透明巨页 (THP):
1 | // THP 配置 |
6. 虚拟内存区域 (VMA)
6.1 vm_area_struct 结构
位置: include/linux/mm_types.h
1 | struct vm_area_struct { |
6.2 VMA 标志
定义: include/linux/mm.h
1 | // VMA 标志 |
6.3 VMA 操作
位置: include/linux/mm.h
1 | struct vm_operations_struct { |
6.4 VMA 管理操作
查找 VMA:
1 | // 查找包含地址的 VMA |
创建/删除 VMA:
1 | // 插入 VMA |
7. 页面分配
7.1 alloc_pages 函数族
函数列表:
1 | // 分配 2^order 个页面 |
7.2 __get_free_pages
函数:
1 | // 分配并返回内核虚拟地址 (直接映射区) |
7.3 释放页面
函数:
1 | // 释放页面 |
7.4 页面引用计数
操作:
1 | // 增加引用计数 |
8. Slab 分配器
8.1 Slab 分配器概述
设计目的:
- 减少内存碎片
- 提高分配速度
- 缓存常用对象
三种实现:
- SLAB - 原始实现
- SLUB - 默认实现 (简化、性能更好)
- SLOB - 针对小型系统
8.2 kmem_cache 结构
位置: include/linux/slab_def.h
1 | struct kmem_cache { |
8.3 Slab 分配 API
创建/销毁缓存:
1 | // 创建缓存 |
分配/释放对象:
1 | // 从缓存分配对象 |
通用分配器:
1 | // 通用 kmalloc (大小必须 <= 2^(MAX_ORDER-1) 页面) |
8.4 SLUB 实现
SLUB 特点:
- 简化的 slab 实现
- 更好的 per-CPU 缓存
- 更少的内存开销
结构:
1 | struct kmem_cache_cpu { |
9. 缺页异常处理
9.1 缺页异常流程
x86_64 缺页异常:
1 | ; 页错误 (#PF) - 异常向量 14 |
处理流程:
1 | 用户/内核访问无效地址 |
9.2 匿名页面缺页
位置: mm/memory.c:do_anonymous_page()
1 | vm_fault_t do_anonymous_page(struct vm_fault *vmf) |
9.3 文件映射缺页
位置: mm/filemap.c:filemap_fault()
1 | vm_fault_t filemap_fault(struct vm_fault *vmf) |
9.4 写时复制
位置: mm/memory.c:do_wp_page()
1 | vm_fault_t do_wp_page(struct vm_fault *vmf) |
9.5 堆栈扩展
位置: mm/mmap.c:expand_stack()
1 | int expand_stack(struct vm_area_struct *vma, unsigned long address) |
10. 页面回收
10.1 LRU 链表
LRU 类型:
1 | enum lru_list { |
LRU 链表结构:
1 | ┌──────────────────────────────────────────────────────────────┐ |
10.2 页面状态转换
状态机:
1 | ┌──────────────────────────────────────────────────────────────────┐ |
10.3 kswapd 内核线程
位置: mm/vmscan.c:kswapd()
1 | static int kswapd(void *p) |
回收触发条件:
1 | // 检查是否需要唤醒 kswapd |
10.4 直接回收
位置: mm/vmscan.c:do_try_to_free_pages()
1 | unsigned long do_try_to_free_pages(struct zonelist *zonelist, |
10.5 页面回收算法
扫描控制:
1 | struct scan_control { |
11. 内存映射
11.1 mmap 系统调用
位置: mm/mmap.c:ksys_mmap_pgoff()
1 | SYSCALL_DEFINE6(mmap, unsigned long, addr, unsigned long, len, |
11.2 mmap 标志
标志定义:
1 |
11.3 mprotect 系统调用
位置: mm/mprotect.c
1 | SYSCALL_DEFINE3(mprotect, unsigned long, start, size_t, len, |
11.4 munmap 系统调用
位置: mm/mmap.c:vm_munmap()
1 | int vm_munmap(unsigned long start, size_t len) |
12. 匿名内存与页缓存
12.1 匿名内存
特点:
- 不与文件关联
- 使用 swap 作为后备存储
- 包括堆、栈、mmap(MAP_ANONYMOUS)
分配:
1 | // brk 系统调用 |
缺页处理:
1 | // 位置: mm/memory.c:do_anonymous_page() |
12.2 页缓存
位置: mm/filemap.c
address_space 结构:
1 | struct address_space { |
页缓存操作:
1 | // 查找页面 |
12.3 页回写
位置: mm/page-writeback.c
回写触发:
1 | // 当脏页达到阈值时触发 |
脏页阈值:
1 | # /proc/sys/vm 参数 |
13. 内存压缩与迁移
13.1 内存压缩
位置: mm/compaction.c
目的:
- 减少内存碎片
- 创建连续的大块内存
- 满足高阶分配请求
压缩原理:
1 | 压缩前: |
压缩扫描:
1 | // 位置: mm/compaction.c |
13.2 页迁移
位置: mm/migrate.c
用途:
- NUMA 负载均衡
- 内存热插拔
- 页面迁移 (mbind, move_pages)
迁移流程:
1 | // 迁移单个页面 |
13.3 kcompactd
位置: mm/compaction.c
内核线程:
1 | static int kcompactd(void *p) |
14. 内存控制组 (Memory Cgroup)
14.1 memcg 概述
用途:
- 限制一组进程的内存使用
- 统计内存使用
- OOM 控制
14.2 mem_cgroup 结构
位置: include/linux/memcontrol.h
1 | struct mem_cgroup { |
14.3 memcg API
创建/删除:
1 | // 创建 memcg |
内存分配:
1 | // 分配时检查 memcg 限制 |
14.4 memcg 统计
统计接口:
1 | // 获取内存使用 |
15. 内存统计与调试
15.1 内存统计
全局统计:
1 | # /proc/meminfo |
每个进程统计:
1 | # /proc/[pid]/status |
15.2 调试工具
slabtop:
1 | # 显示 slab 缓存信息 |
vmstat:
1 | # 显示虚拟内存统计 |
pmap:
1 | # 显示进程内存映射 |
15.3 调试选项
内核配置:
1 | # 内存调试 |
附录 A: 系统调用参考
mmap 相关
| 系统调用 | 描述 | 头文件 |
|---|---|---|
mmap() |
创建内存映射 | sys/mman.h |
mmap2() |
创建内存映射 (32位) | sys/mman.h |
munmap() |
删除内存映射 | sys/mman.h |
mprotect() |
修改内存保护 | sys/mman.h |
msync() |
同步内存映射到文件 | sys/mman.h |
mremap() |
重新映射内存 | sys/mman.h |
mlock() |
锁定内存 | sys/mman.h |
munlock() |
解锁内存 | sys/mman.h |
mlockall() |
锁定所有内存 | sys/mman.h |
munlockall() |
解锁所有内存 | sys/mman.h |
mincore() |
检查页面是否在内存中 | sys/mman.h |
madvise() |
给出内存使用建议 | sys/mman.h |
remap_file_pages() |
重新映射文件页面 | sys/mman.h |
brk 相关
| 系统调用 | 描述 | 头文件 |
|---|---|---|
brk() |
改变数据段大小 | unistd.h |
sbrk() |
改变数据段位置 | unistd.h |
其他内存相关
| 系统调用 | 描述 | 头文件 |
|---|---|---|
mbind() |
设置内存策略 | numaif.h |
get_mempolicy() |
获取内存策略 | numaif.h |
set_mempolicy() |
设置内存策略 | numaif.h |
move_pages() |
移动页面 | numaif.h |
migrate_pages() |
迁移页面 | numaif.h |
附录 B: /proc 和 /sys 接口
/proc 接口
1 | # 全局内存信息 |
/sys 接口
1 | # 节点信息 |
附录 C: 相关配置选项
1 | # 内存模型 |
- Title: Linux内核分析之内存管理-00
- Author: 韩乔落
- Created at : 2026-01-20 21:37:20
- Updated at : 2026-02-24 14:05:34
- Link: https://jelasin.github.io/2026/01/20/Linux内核分析之内存管理-00/
- License: This work is licensed under CC BY-NC-SA 4.0.