一、整体架构概览
内存管理子系统层次结构
用户空间进程
│
▼
系统调用接口
│
├── mmap() ──┐
├── brk() │
├── malloc()│
└── 其他内存相关系统调用
│
▼
虚拟内存管理器 (Virtual Memory Manager)
├── 地址空间管理 (mm_struct)
├── 虚拟内存区域管理 (vm_area_struct)
├── 页表管理
└── 缺页异常处理
│
▼
物理内存管理器
├── 伙伴系统 (Buddy System)
├── SLAB/SLUB分配器
├── 每CPU页框缓存
└── 内存区域管理 (ZONE)
│
▼
硬件抽象层
├── MMU接口
├── TLB管理
└── 架构特定代码
二、核心数据结构与源码组成
1. 物理页框管理 (include/linux/mm_types.h)
struct page {
unsigned long flags; /* 页框状态标志 */
union {
struct { /* 页框缓存和匿名页 */
struct list_head lru; /* LRU链表 */
struct address_space *mapping; /* 地址空间 */
pgoff_t index; /* 页内偏移 */
void *private; /* 私有数据 */
};
// ... 其他联合体成员
};
atomic_t _refcount; /* 引用计数 */
atomic_t _mapcount; /* 页表映射计数 */
unsigned long compound_head; /* 复合页头 */
unsigned int compound_order; /* 复合页阶数 */
};
2. 内存区域划分 (include/linux/mmzone.h)
/* 内存区域类型 */
enum zone_type {
ZONE_DMA, /* DMA区域 */
ZONE_DMA32, /* 32位DMA区域 */
ZONE_NORMAL, /* 普通内存区域 */
ZONE_HIGHMEM, /* 高端内存 */
ZONE_MOVABLE, /* 可移动区域 */
ZONE_DEVICE, /* 设备内存 */
__MAX_NR_ZONES
};
/* 区域描述符 */
struct zone {
unsigned long watermark[NR_WMARK]; /* 水位线 */
long lowmem_reserve[MAX_NR_ZONES]; /* 保留内存 */
struct pglist_data *zone_pgdat; /* 所属节点 */
struct per_cpu_pageset __percpu *pageset; /* 每CPU页框缓存 */
/* 空闲区域管理 */
struct free_area free_area[MAX_ORDER];
/* 统计信息 */
atomic_long_t vm_stat[NR_VM_ZONE_STAT_ITEMS];
atomic_long_t vm_numa_stat[NR_VM_NUMA_STAT_ITEMS];
unsigned long zone_start_pfn; /* 起始页框号 */
unsigned long managed_pages; /* 管理页数 */
unsigned long spanned_pages; /* 跨越页数 */
unsigned long present_pages; /* 实际页数 */
const char *name; /* 区域名称 */
wait_queue_head_t *wait_table; /* 等待队列 */
// ...
};
3. NUMA节点管理
typedef struct pglist_data {
struct zone node_zones[MAX_NR_ZONES];
struct zonelist node_zonelists[MAX_ZONELISTS];
int nr_zones;
unsigned long node_start_pfn;
unsigned long node_present_pages;
unsigned long node_spanned_pages;
int node_id;
struct pglist_data *node_next;
/* 内存统计 */
struct lruvec lruvec;
/* 回收相关 */
unsigned long min_unmapped_pages;
unsigned long min_slab_pages;
/* 每节点页框缓存 */
struct per_cpu_nodestat __percpu *per_cpu_nodestats;
} pg_data_t;
4. 地址空间管理 (include/linux/mm_types.h)
struct mm_struct {
struct vm_area_struct *mmap; /* VMA链表 */
struct rb_root mm_rb; /* VMA红黑树 */
unsigned long start_code, end_code; /* 代码段边界 */
unsigned long start_data, end_data; /* 数据段边界 */
unsigned long start_brk, brk; /* 堆边界 */
unsigned long start_stack; /* 栈起始 */
unsigned long arg_start, arg_end; /* 命令行参数 */
unsigned long env_start, env_end; /* 环境变量 */
unsigned long total_vm; /* 总虚拟内存 */
unsigned long locked_vm; /* 锁定内存 */
unsigned long pinned_vm; /* 固定内存 */
unsigned long data_vm; /* 数据内存 */
unsigned long exec_vm; /* 可执行内存 */
unsigned long stack_vm; /* 栈内存 */
spinlock_t page_table_lock; /* 页表锁 */
struct rw_semaphore mmap_sem; /* MMAP信号量 */
pgd_t *pgd; /* 页全局目录 */
atomic_t mm_users; /* 用户计数 */
atomic_t mm_count; /* 引用计数 */
/* 反向映射 */
struct {
struct vm_area_struct *mmap; /* 链表 */
struct rb_root rb_root; /* 红黑树 */
} mm_mt;
// ...
};
5. 虚拟内存区域 (include/linux/mm_types.h)
struct vm_area_struct {
unsigned long vm_start; /* 起始地址 */
unsigned long vm_end; /* 结束地址 */
struct mm_struct *vm_mm; /* 所属地址空间 */
pgprot_t vm_page_prot; /* 访问权限 */
unsigned long vm_flags; /* 标志位 */
struct rb_node vm_rb; /* 红黑树节点 */
union {
struct {
struct rb_node rb;
unsigned long rb_subtree_last;
} shared;
struct anon_vma_name *anon_name;
};
struct list_head anon_vma_chain; /* 匿名VMA链 */
struct anon_vma *anon_vma; /* 匿名VMA */
const struct vm_operations_struct *vm_ops; /* 操作函数 */
unsigned long vm_pgoff; /* 文件偏移 */
struct file *vm_file; /* 映射文件 */
void *vm_private_data; /* 私有数据 */
struct vm_area_struct *vm_next, *vm_prev; /* 链表 */
};
三、关键设计原理
1. 三级内存分配机制
1.1 伙伴系统 (mm/page_alloc.c)
/* 伙伴系统核心数据结构 */
struct free_area {
struct list_head free_list[MIGRATE_TYPES]; /* 空闲链表 */
unsigned long nr_free; /* 空闲页数 */
};
设计原理:
- 将空闲内存分为11个不同阶的链表(0-10阶,对应1, 2, 4, ..., 1024个连续页)
- 分配时从合适阶的链表获取,如果没有则从更高阶拆分
- 释放时检查相邻块是否空闲,如空闲则合并
1.2 SLAB分配器 (mm/slab.c, mm/slub.c, mm/slob.c)
struct kmem_cache {
unsigned int size; /* 对象大小 */
unsigned int object_size; /* 实际对象大小 */
unsigned int offset; /* 空闲指针偏移 */
struct kmem_cache_node *node[MAX_NUMNODES];
struct array_cache *cpu_cache; /* 每CPU缓存 */
/* SLAB管理 */
unsigned int colour; /* 缓存行着色 */
unsigned int colour_off; /* 着色偏移 */
unsigned int num; /* 每SLAB对象数 */
/* 构造函数/析构函数 */
void (*ctor)(void *obj);
// ...
};
设计原理:
- SLAB:为内核对象提供缓存,减少内存分配开销
- SLUB:简化设计,更好性能,默认分配器
- SLOB:适用于嵌入式系统,简单但碎片多
2. 按需分页与写时复制
2.1 缺页异常处理 (mm/memory.c)
static vm_fault_t handle_pte_fault(struct vm_fault *vmf)
{
if (!vmf->pte) {
if (vma_is_anonymous(vmf->vma))
return do_anonymous_page(vmf); /* 匿名页 */
else
return do_fault(vmf); /* 文件映射 */
}
if (vmf->flags & FAULT_FLAG_WRITE) {
if (!pte_write(entry))
return do_wp_page(vmf); /* 写时复制 */
entry = pte_mkdirty(entry);
}
// ...
}
2.2 写时复制实现
static vm_fault_t do_wp_page(struct vm_fault *vmf)
{
struct vm_area_struct *vma = vmf->vma;
struct page *old_page, *new_page;
/* 获取旧页 */
old_page = vm_normal_page(vma, vmf->address, vmf->pte);
/* 如果是零页或独占映射,直接设置可写 */
if (PageAnon(old_page) && PageAnonExclusive(old_page)) {
ptep_set_wrprotect(vma->vm_mm, vmf->address, vmf->pte);
return 0;
}
/* 分配新页 */
new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vmf->address);
/* 复制内容 */
copy_user_highpage(new_page, old_page, vmf->address, vma);
/* 建立新映射 */
ptep_clear_flush(vma, vmf->address, vmf->pte);
page_add_new_anon_rmap(new_page, vma, vmf->address, false);
set_pte_at(vma->vm_mm, vmf->address, vmf->pte,
mk_pte(new_page, vma->vm_page_prot));
/* 释放旧页引用 */
put_page(old_page);
return 0;
}
3. 内存回收机制 (LRU算法)
3.1 LRU链表管理 (mm/vmscan.c)
enum lru_list {
LRU_INACTIVE_ANON = LRU_BASE,
LRU_ACTIVE_ANON = LRU_BASE + LRU_ACTIVE,
LRU_INACTIVE_FILE = LRU_BASE + LRU_FILE,
LRU_ACTIVE_FILE = LRU_BASE + LRU_FILE + LRU_ACTIVE,
LRU_UNEVICTABLE,
NR_LRU_LISTS
};
设计原理:
- 双LRU链表:活跃链表和非活跃链表
- 页面访问时移动到活跃链表尾部
- 回收时从非活跃链表头部选择页面
3.2 反向映射 (Reverse Mapping)
struct anon_vma {
struct rw_semaphore rwsem; /* 读写信号量 */
atomic_t refcount; /* 引用计数 */
struct anon_vma *root; /* 根节点 */
struct rb_root_cached rb_root; /* 红黑树根 */
};
作用:快速找到映射到某个物理页的所有页表项,用于页面回收和迁移
4. 透明大页 (THP) 支持
4.1 大页分配 (mm/huge_memory.c)
static int do_huge_pmd_anonymous_page(struct vm_fault *vmf)
{
struct vm_area_struct *vma = vmf->vma;
gfp_t gfp;
/* 检查是否启用THP */
if (!transparent_hugepage_enabled(vma))
return VM_FAULT_FALLBACK;
/* 分配大页 */
gfp = alloc_hugepage_direct_gfpmask(vma);
page = alloc_hugepage_vma(gfp, vma, haddr, HPAGE_PMD_ORDER);
/* 清零大页 */
clear_huge_page(page, vmf->address, HPAGE_PMD_NR);
/* 建立映射 */
set_huge_zero_page(pgtable, vma, page, haddr);
return 0;
}
四、关键点说明
1. 内存分配策略
1.1 GFP标志 (include/linux/gfp.h)
/* 分配器标志 */
#define GFP_KERNEL __GFP_RECLAIM
#define GFP_ATOMIC (__GFP_HIGH|__GFP_ATOMIC|__GFP_KSWAPD_RECLAIM)
#define GFP_USER __GFP_WAIT
#define GFP_HIGHUSER (GFP_USER | __GFP_HIGHMEM)
#define GFP_NOFS (__GFP_WAIT | __GFP_IO)
#define GFP_NOIO __GFP_WAIT
1.2 水位线机制
enum zone_watermarks {
WMARK_MIN, /* 最低水位线 - 开始回收 */
WMARK_LOW, /* 低水位线 - 开始压缩 */
WMARK_HIGH, /* 高水位线 - 停止回收 */
NR_WMARK
};
2. 内存压缩与碎片整理
2.1 内存压缩 (mm/compaction.c)
static int compact_zone(struct zone *zone, struct compact_control *cc)
{
while ((ret = isolate_migratepages(cc)) == ISOLATE_ABORT) {
migrate_pages(&cc->migratepages, compaction_alloc,
compaction_free, (unsigned long)cc,
cc->mode, MR_COMPACTION);
}
}
2.2 内存碎片整理策略
- 内存规整:移动页面以创建连续大块内存
- 页迁移:迁移正在使用的页面
- 回收不可移动页面:尝试回收锁定页面
3. NUMA优化
3.1 NUMA感知分配
static struct page *alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order,
int preferred_nid, nodemask_t *nodemask)
{
/* 优先从本地节点分配 */
if (preferred_nid >= 0)
page = __alloc_pages(gfp_mask, order,
zonelist_zone_idx(zonelist, preferred_nid));
/* 回退到其他节点 */
if (!page)
page = __alloc_pages(gfp_mask, order, NUMA_NO_NODE);
return page;
}
4. 内存控制组 (Cgroups) 集成
4.1 内存控制器 (mm/memcontrol.c)
struct mem_cgroup {
struct page_counter memory; /* 内存使用 */
struct page_counter swap; /* 交换空间 */
struct page_counter kmem; /* 内核内存 */
/* 每节点统计 */
struct mem_cgroup_per_node *nodeinfo[MAX_NUMNODES];
/* 回收参数 */
unsigned long high; /* 高水位线 */
unsigned long low; /* 低水位线 */
unsigned long min; /* 最小值 */
/* OOM控制 */
oom_control_t oom;
};
五、重要源码文件说明
mm/
├── page_alloc.c # 伙伴系统实现
├── slab.c # SLAB分配器
├── slub.c # SLUB分配器
├── vmscan.c # 页面回收
├── memory.c # 核心内存管理
├── mmap.c # 内存映射
├── mlock.c # 内存锁定
├── mremap.c # 内存重映射
├── swap.c # 交换管理
├── swap_state.c # 交换状态
├── swapfile.c # 交换文件
├── vmalloc.c # 虚拟内存分配
├── hugetlb.c # 大页支持
├── huge_memory.c # 透明大页
├── compaction.c # 内存压缩
├── memcontrol.c # 内存控制组
├── mempool.c # 内存池
├── kmemleak.c # 内存泄漏检测
└── ksm.c # 内核同页合并
六、调优参数与性能优化
1. 内核参数调优
# /proc/sys/vm/ 下重要参数
vm.swappiness = 60 # 交换倾向
vm.vfs_cache_pressure = 100 # 文件缓存压力
vm.dirty_ratio = 20 # 脏页比例阈值
vm.dirty_background_ratio = 10 # 后台回写阈值
vm.min_free_kbytes = 67584 # 最小空闲内存
vm.watermark_scale_factor = 10 # 水位线缩放因子
2. 透明大页配置
# 查看THP状态
cat /sys/kernel/mm/transparent_hugepage/enabled
# 配置选项
echo madvise > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/enabled
3. NUMA策略
# 查看NUMA状态
numastat
numactl --hardware
# 设置NUMA策略
numactl --cpunodebind=0 --membind=0 ./program
七、总结
Linux内核内存管理是一个复杂而精密的系统,其设计特点包括:
- 层次化设计:从硬件抽象到用户接口的清晰分层
- 按需分配:延迟分配和写时复制提高内存利用率
- 缓存优化:多级缓存结构减少分配开销
- 智能回收:基于LRU的回收算法平衡性能与内存使用
- NUMA感知:优化非均匀内存访问架构性能
- 容器支持:通过Cgroups提供资源隔离
理解这些设计原理对于系统调优、性能分析和问题排查至关重要。在实际应用中,需要根据具体工作负载调整内存管理参数,以达到最佳性能。