一、物理内存管理的基石:Buddy System 伙伴系统

1.1 核心思想与 free_area 结构

Linux内核物理内存管理的核心数据结构是Buddy System(伙伴系统)。每个NUMA节点(pglist_data)内含多个管理区域(zone),每个zone(如ZONE_DMA、ZONE_NORMAL、ZONE_HIGHMEM)维护一个 free_area[MAX_ORDER] 数组,每个数组元素包含一个 free_list[__MAX_NR_MIGRATETYPES] 链表。

分配阶(order)从0(单页4KB)到MAX_ORDER-1(通常10,即1024页=4MB),每次分配需要2order个连续物理页。当低阶空闲页不足时,会将高阶页面分裂为两个"伙伴"(buddy),递归向下分配;释放时则检查伙伴是否空闲-若空闲则合并为高阶块,循环往复。

1.2 伙伴算法的关键操作

核心函数:__rmqueue_smallest()(优先取最小可用阶以最小化碎片)→ __rmqueue_fallback()(启用迁移类型回退算法,对不可动页面从可回收/可动区域借页)→ __alloc_pages_nodemask()(顶层入口)。

伙伴算法的伙伴计算:伙伴页框号 = page_idx XOR (1 << order)。仅需一次XOR运算即可定位伙伴,O(1)时间复杂度。

1.3 伙伴系统的碎片问题

Buddy System天然产生外部碎片(空闲页不连续)。Linux通过迁移类型(migrate type)将页面分为MIGRATE_UNMOVABLE(内核数据)、MIGRATE_MOVABLE(用户页)、MIGRATE_RECLAIMABLE(可回收页,如页缓存),使同类页面聚集,极大减少无法合并的碎片。

二、SLAB / SLUB / SLOB 内核对象分配器

2.1 为什么需要 Slab 分配器?

Buddy System以页(4KB)为粒度分配,内核频繁创建销毁task_struct、inode、dentry等远小于4KB的对象。纯页分配将造成严重内部碎片(一个500B对象独占4KB)。Slab解决此问题:将一页或多个页切分为若干等大对象槽(object),精确匹配内核结构体大小。

此外,Slab复用已释放对象,保留初始化状态(对象缓存),避免反复构造/析构的CPU开销。任务切换时task_struct复用频率极高,Slab缓存效果显著。

2.2 SLAB / SLUB / SLOB 三种变体对比

SLAB:经典实现,每个Slab页维护本地freelist指针,kmem_cache内含三层队列(full/partial/partial为空时从partial调拨)。缺点:元数据过多,kmem_cache结构体庞大(数百字节),多核扩展性差,NUMA支持复杂。

SLUB——Linux默认:大幅简化,每个CPU维护本地部分页(cpu_slab→freelist + page),NUMA节点维护node部分页(kmem_cache_node→partial链表),全局per-CPU设计使常见缓存分配无锁(fast path仅需preempt_disable+cmpxchg)。

SLOB:极端精简版,用于嵌入式无MMU系统,使用首次适应分配(first-fit),已逐步淡出。

2.3 SLUB 分配流程详解

快速路径(fast path,无锁):

  • 检查page→freelist,若有空闲对象,cmpxchg原子摘取一个返回 → O(1)
  • 无空闲对象时检查CPU partial链表,可切换新cpu_slab → 常数时间

慢速路径(慢路径,需加zone锁):

  • 从NUMA node的partial链表取半满页补充cpu partial → 可能触发同node间页迁移
  • Node也无partial时向Buddy System申请新页(alloc_pages_node)

释放时反向:freelist重挂→检查page满不满→全满页归还到node partial或直接释放给Buddy。

2.4 SLUB 关键配置与调试

/proc/slabinfo查看各cache统计;slub_debug=UFP启用User poisoning、Freed poisoning、Red zone防护;sysctl vm.min_free_kbytes控制伙伴系统保留阈值。SLUB支持(CONFIG_SLUB_CPU_PARTIAL)做per-CPU partial缓存,显著减少流水线上的原子操作。

三、虚拟内存与多级页表

3.1 四级(五级)页表架构(x86-64 / AArch64)

Linux在内核态使用四级甚至五级页表。x86-64标准四级:PML4→PDPT→PD→PT(每级512项×8B),覆盖48位虚拟地址(256TB)。内核页表(swapper_pg_dir)通过TTBR1_EL1(ARM)或内核态CR3映射高地址一半(0xFFFF...)。

五级页表(LA57、PML5)扩展至57位(128PB),新增第五级PML5,内核通过CONFIG_X86_5LEVEL启用。

3.2 页表项(PTE)标志位语义

硬件PTE包含:物理PFN(40-52位)、Present(有效位)、RW(读写)、用户/内核态U/S、PCD/PWT(缓存策略)、Accessed(访问位)、Dirty(脏位)、NX(64位不可执行,DEP保护)。

Linux复用硬件Present位编码特殊状态:硬件P=0可能是swap、未映射(PROT_NONE)、文件映射缺页(demand paging)或完成了写时复制(COW),均会触发do_page_fault→handle_mm_fault→特定case路由。

3.3 页缓存与映射(VMA / page cache)

VMA:每个进程的mm_struct通过红黑树/VMA链表记录虚拟地址区间(vm_start/vm_end外,vm_page_prot等)。缺页或访问权限错误时,内核查找覆盖该VA的VMA决定处理路径。

Page Cache:文件内容页缓存通过radix tree(现已演进为XArray)按文件inode+index索引。read()经vfs→page cache hit则零磁盘命中;mmap()建立VMA→file backed映射,后续访问由page fault从page cache填充PTE。

共享映射(MAP_SHARED)修改回写磁盘;私有映射(MAP_PRIVATE)触发COW——写时复制副本。fork后父子共享只读PTE,写触发pte_wrprotect+COW page fault,内核分配新页→复制内容→更新PTE→原页put_page减引用。

四、NUMA 感知内存管理

4.1 NUMA 拓扑感知

Linux每个NUMA node由 pg_data_t(pglist_data)描述,包含其node_zones、node_zonelists(分配回退顺序)、node_mem_map(页面数组)。ACPI SRAT表or设备树numa-distance定义node间距离,影响跨节点访问惩罚权重。

4.2 NUMA 策略与 numactl

进程可以通过mbind/mmap + MPOL_PREFERRED/MPOL_BIND/MPOL_INTERLEAVE等策略控制分配来源node。numactl --cpunodebind=0 --membind=0 ./app将分配绑定到node 0。内核态:GFP_THISNODE强制本地node分配;shmem_default_policy控制tmpfs最分配策略。

4.3 NUMA Balancing(自动页面迁移)

Linux内核自4.x引入NUMA Balancing迁移线程(khugepaged + numa_balancing)。周期性扫描标记PTE的Accessed位,若远程访问比例超阈值(/sys/kernel/numa_balancing_scan_period_min_ms),内核缺页中断中set_pte_at将页迁移到访问者所在node,并错误flip PTE到PFN指向新页。

五、实战:性能调优与问题排查

5.1 OOM Killer 决策逻辑

内核内存耗尽且回收无效时调用select_bad_process(),用oom_badness评分(RSS+swapenas + oom_score_adj × factor),分数最高者被选kill。/proc/[pid]/oom_score_adj范围-1000到1000,设为-1000则免疫。memory.oom_group让同一cgroup整体被杀(防止误杀分页)。

5.2 内存碎片监控与反碎片

/proc/buddyinfo展示各阶空闲块数;/proc/pagetypeinfo含迁移类型分布;compact_memory触发手动内存规整(__compact_zone强制扫描)。CONFIG_COMPACTION + /proc/sys/vm/compact_memory,使THP大页分配成功率显著提升。

5.3 性能工具链

  • perf stat -e dTLB-load-misses,iTLB-load-misses:TLB命中率
  • numastat:per-node命中/跨节点统计
  • slabtop:实时Slab对象Top
  • vmstat 1:si/so(交换进出)+ free/buff/cache
  • bpftrace/bcc memleak:内核态和用户态内存泄漏追踪

5.4 THP(Transparent Huge Pages)

内核自动将连续4KB页映射为2MB透明大页(amd64 / 1GB if hardware),减少TLB压力和walk深度。开启模式:/sys/kernel/mm/transparent_hugepage/enabled(always/madvise)。madvise(addr,len,MADV_HUGEPAGE)提示应用明确请求。SHA异步khugepaged规整碎片为THP;MADV_DONTNEED释放物理回Buddy。

六、总结:Linux内存管理技术全景

Buddy System应对物理页粒度的外部碎片,迁移类型与回借算法维持大页可用;SLUB分配器解决内核对象级碎片与反复构造开销,per-CPU无锁极速路径高性能基石;多级页表实现高效虚拟到物理转换,TLP+THP扩大映射扇出降低miss;NUMA感知+自动迁移则让多路服务器内存行为更贴近延迟最优路径。

理解这些子系统的交互,是解决高压力服务器抖动、OOM误杀、缓存抖动等关键稳定性问题的必经之路。建议开发同学结合slabtop、numastat、perf bpftrace组合探查,将理论知识转化为实际诊断肌肉记忆。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿
网站二维码

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部