Linux内核内存管理深度实战:从Buddy System到Slab分配器
引言
Linux内核的内存管理是操作系统中最复杂且最核心的子系统之一。本文将深入剖析Linux内核内存管理的核心机制——Buddy System(伙伴系统)和Slab分配器,并通过实际代码示例展示其工作原理。
一、Buddy System(伙伴系统)核心原理
1.1 算法思想
Buddy System将物理内存划分为不同阶(order)的块组,每个阶包含大小为2^order个连续物理页的内存块。分配时从合适的阶取块,释放时若相邻"伙伴"空闲则合并。
// free_area结构(简化)
struct free_area {
struct free_list free_list[MIGRATE_TYPES];
unsigned long nr_free;
};
// 11个阶:0=1页(4KB) ... 10=1024页(4MB)
#define MAX_ORDER 11
1.2 伙伴计算与碎片管理
伙伴页号通过XOR快速计算:buddy_pfn = pfn ^ (1 << order)
外部碎片通过页面迁移类型缓解:
- MIGRATE_UNMOVABLE:内核数据
- MIGRATE_MOVABLE:用户页面
- MIGRATE_RECLAIMABLE:页缓存
二、Slab分配器深入剖析
2.1 为什么需要Slab
伙伴系统以4KB页为最小单位,但内核需要分配小于4KB的对象(task_struct、inode等)。Slab在页之上建立对象缓存池。
// slab结构(Linux 5.x)
struct slab {
void *freelist; // 空闲对象链表
unsigned inuse; // 已使用数
unsigned objects; // 总对象数
struct list_head slab_list;
};
2.2 SLUB — 现代默认分配器
SLUB替代原始Slab,主要改进:
- 元数据嵌入slab,开销更小
- CPU本地缓存(cpu_slab)无锁快速路径
- NUMA本地partial支持
- 合并同尺寸cache减少碎片
2.3 Kmalloc与vmalloc
| API | 连续性 | 大小限制 | 典型场景 |
|---|---|---|---|
| kmalloc | 物理+虚拟连续 | ≤128KB | DMA、驱动 |
| vmalloc | 仅虚拟连续 | 几乎无限 | 大缓冲区、模块加载 |
| kmem_cache_alloc | 物理连续 | 自定义 | 高频小对象 |
三、虚拟内存与页表管理
3.1 x86_64四级页表
Virtual Address (48-bit):
┌─────────┬─────────┬─────────┬─────────┬──────────┐
│PML4(9b) │PDPT(9b) │ PD(9b) │ PT(9b) │Offset(12b)│
└─────────┴─────────┴─────────┴─────────┴──────────┘
512 GB 1 GB 2 MB 4 KB
3.2 TLB与ASID/PCID
- TLB缓存页表项加速地址翻译
- PCID (Process-Context Identifier) 避免进程切换全TLB刷新
- TLB Shootdown:修改页表后通过IPI通知其他CPU失效
四、实战内核模块
// mm_demo.c
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
static struct kmem_cache *my_cache;
static int __init demo_init(void)
{
// 1. 创建自定义缓存
my_cache = kmem_cache_create("my_obj_cache", 256, 0, SLAB_HWCACHE_ALIGN, NULL);
// 2. 从缓存分配
void *obj = kmem_cache_alloc(my_cache, GFP_KERNEL);
// 3. kmalloc分配物理连续内存
void *kptr = kmalloc(1024, GFP_KERNEL);
// 4. vmalloc分配虚拟连续内存
void *vptr = vmalloc(PAGE_SIZE * 16);
return 0;
}
static void __exit demo_exit(void)
{
kmem_cache_free(my_cache, obj);
kfree(kptr);
vfree(vptr);
kmem_cache_destroy(my_cache);
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
五、性能调优实践
5.1 关键内核参数
vm.swappiness = 10 # 数据库推荐1-10
vm.min_free_kbytes = 262144 # 4%物理内存
vm.overcommit_memory = 2 # 严格模式
vm.vfs_cache_pressure = 100 # 越大越回收dentry/inode
kernel.numa_balancing = 0 # 数据库关闭
vm.nr_hugepages = 1024 # 静态大页池
5.2 监控指标
- node_memory_MemAvailable_bytes
- node_vmstat_pgfault / pgmajfault
- node_vmstat_oom_kill
- /proc/buddyinfo 查看碎片
- /proc/slabinfo 查看缓存状态
5.3 常用诊断命令
# 查看Buddy系统
cat /proc/buddyinfo
# 实时slab缓存
slabtop -s c
# 手动碎片整理
echo 1 > /proc/sys/vm/compact_memory
# NUMA拓扑
numactl --hardware
# BPF追踪分配
funclatency -u kmalloc
六、总结
Linux内核内存管理通过Buddy System和Slab/SLUB的协同工作,实现了高效灵活的物理内存管理。理解这些底层机制对于内核开发、驱动编程和系统性能调优至关重要。掌握kmalloc、vmalloc、kmem_cache等API的使用场景,能帮助开发者避免内存泄漏和性能瓶颈。

发表评论 取消回复