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物理+虚拟连续≤128KBDMA、驱动
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的使用场景,能帮助开发者避免内存泄漏和性能瓶颈。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部