引言

Linux 内核的内存管理是操作系统最核心的子系统之一。它不仅负责物理内存的分配与回收,还通过虚拟内存机制为每个进程提供了独立且巨大的地址空间。对于系统管理员和底层开发者而言,深入理解内核内存管理的原理与机制,是排查性能瓶颈、优化系统表现的关键。

本文将从物理页面管理出发,深入剖析 Buddy 分配系统、Slab 分配器、内存碎片化等核心概念,并结合实际案例展示内存调优的实战方法。

一、物理页面管理:一切的基础

物理页面(Page)是内核内存管理的最小单位,通常为 4KB。内核使用 struct page 结构体来跟踪系统中的每一个物理页面。

1.1 页帧与 PFN

每个物理页面都有一个唯一的编号,称为页帧号(Page Frame Number, PFN)。PFN 与物理地址的关系为:

PFN = 物理地址 / PAGE_SIZE

内核通过 pfn_to_page() 和 page_to_pfn() 两个宏实现 PFN 与 struct page 之间的相互转换。

1.2 内存区域(Zone)

由于硬件架构的限制,内核将物理内存划分为不同的区域(Zone):

区域说明典型范围
ZONE_DMADMA 可访问的低端内存0-16MB
ZONE_NORMAL内核可直接线性映射的内存16MB-896MB
ZONE_HIGHMEM动态映射的高端内存896MB以上(32位)

在 64 位系统中,地址空间极其庞大,ZONE_HIGHMEM 不再需要,所有物理内存都直接映射在 ZONE_NORMAL 中。

二、Buddy 系统:伙伴分配算法

Buddy 系统是内核物理页面分配的核心算法,它负责以页为单位进行内存的分配与回收。

2.1 算法原理

Buddy 系统将空闲页面组织为 11 个链表,分别管理 1、2、4、8、...、1024 个连续页面(即阶为 0 到 10)。当请求 N 个页面时:

  1. 找到能满足需求的最小阶(order),使得 2order >= N
  2. 若该阶有空闲块,直接分配
  3. 若没有,向上递归查找更大的空闲块,将其分裂为两半(Buddy),一半分配,另一半放入低阶链表
  4. 释放时,检查相邻的 Buddy 块是否空闲,若空闲则合并为更大的块

2.2 避免碎片化的努力

Buddy 系统天然倾向于产生外部碎片——即由于分配和释放的随机性,导致空闲页面散落在不连续的区域中。为此,Linux 引入了可移动页面分组机制:

  • 不可移动:内核数据结构(如页表、task_struct)
  • 可回收:页缓存、文件映射页面
  • 可移动:用户空间页面、可重定位的内核分配

在页面回收时,将相似类型的页面聚集在一起,减少碎片化程度。

三、Slab 分配器:小对象的高效管理

Buddy 系统以页为最小单位,但内核经常需要分配远小于 4KB 的对象(如 task_struct、inode、dentry 等)。若直接通过 Buddy 分配整页,将造成严重的内部碎片。

3.1 Slab 设计思想

Slab 分配器在 Buddy 系统之上构建了一层缓存(Cache),每个缓存专门管理某一类大小的对象:

  1. Slab:从 Buddy 分配器获取一个或多个页面,划分为固定大小的对象槽
  2. Cache:管理多个 slab,每个 cache 对应一种对象类型
  3. Constructor / Destructor:对象构造和析构函数,实现初始化复用

3.2 Slab 的状态

每个 slab 处于三种状态之一:

  • Full:所有对象槽都被分配
  • Partial:部分空闲,优先从此分配新对象
  • Empty:全部空闲,内存紧张时返还给 Buddy 系统

内核优先从 Partial slab 分配对象,若不存在则从 Buddy 获取新页面创建 slab。

3.3 SLAB / SLUB / SLOB 三种实现

实现特点适用场景
SLAB经典实现,丰富的调试选项早期内核
SLUB当前默认实现,简化元数据、更好的多核扩展性通用场景
SLOB极简实现,内存开销极小嵌入式系统

四、虚拟内存与页表

每个进程拥有独立的虚拟地址空间,内核通过将虚拟地址映射到物理地址实现内存隔离。

4.1 多级页表

x86_64 架构使用四级页表:

PML4 → PDPT → PD → PT → Page

每级页表占用 9 位地址(512 项),最终指向 4KB 页面。这种多级结构使得未使用的地址区域无需分配中间页表,极大节省内存。

4.2 大页(Huge Pages)

传统页表只能映射 4KB 页面,对于需要大型连续内存的数据库应用,页表项数量巨大,TLB(Translation Lookaside Buffer)命中率低。大页机制允许使用 2MB 或 1GB 的大页面:

  • 2MB 大页:跳过 PT 层,PD 项直接指向 2MB 物理页面
  • 1GB 大页:跳过 PD 层,PDPT 项直接指向 1GB 物理页面

使用大页可显著提升 TLB 命中率,数据库场景下性能提升可达 30%。

五、页面回收与内存压力管理

当系统内存不足时,内核需要回收页面以释放内存。

5.1 LRU 链表

Linux 使用改进的 LRU(Least Recently Used)算法,将页面分为:

  • Active 链表:最近被访问过的页面
  • Inactive 链表:较长时间未被访问的页面

页面在两个链表之间移动:长时间未被访问的 Active 页面降级为 Inactive;Inactive 页面被重新访问则升级为 Active。回收时优先从 Inactive 链表尾部拿取页面。

5.2 kswapd 守护进程

kswapd 是内核的页面回收守护进程,它在后台周期性检查内存水位:

  • High:内存充足,kswapd 休眠
  • Low:内存开始紧张,kswapd 开始异步回收
  • Min:内存极度紧张,触发直接回收(direct reclaim),分配进程自身阻塞并执行回收

直接回收会严重影响应用性能,应尽量避免。

5.3 OOM Killer

当直接回收仍无法获得足够内存时,内核触发 OOM Killer,根据算法计算每个进程的 "badness score",杀死得分最高的进程以缓解内存压力。

可以通过 /proc/<pid>/oom_score_adj 调整进程的 OOM 优先级,保护关键服务。

六、内存调优实战案例

6.1 数据库场景:减少直接回收

某 MySQL 服务器在高并发写入时出现频繁抖动,vmstat 1 显示 si/so(swap in/out)数值极高,sar -B 1 显示 pgscank(kswapd 扫描速率)飙升。

解决方案:

# 降低 swappiness,减少页缓存回收倾向
echo 10 > /proc/sys/vm/swappiness

# 提高 min_free_kbytes,提前唤醒 kswapd
echo 262144 > /proc/sys/vm/min_free_kbytes   # 256MB

# 设置 MySQL 的 OOM 保护
echo -1000 > /proc/$(pidof mysqld)/oom_score_adj

6.2 透明大页(THP)的双面性

透明大页(Transparent Huge Pages)尝试自动将小页面合并为大页,但某些场景下会导致延迟尖刺:

# 查看 THP 状态
cat /sys/kernel/mm/transparent_hugepage/enabled

# 对于延迟敏感的实时应用,禁用 THP
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag

数据库(如 Redis、MongoDB)通常建议禁用 THP,避免内存合并操作引起的延迟抖动。

6.3 内存碎片化排查

使用 grep -i "fragment" /proc/buddyinfo 检查内存碎片化程度。若高阶连续页面(order >= 3)接近 0,说明碎片化严重。

缓解措施:

# 手动触发内存整理
echo 1 > /proc/sys/vm/compact_memory

# 调整 compaction 激进程度
echo 100 > /proc/sys/vm/extfrag_threshold

七、关键参数速查表

参数默认值说明
vm.swappiness60Swap 倾向,0=尽量不swap,100=积极swap
vm.dirty_ratio20脏页占总内存的最大百分比
vm.dirty_background_ratio10后台回写启动的百分比阈值
vm.min_free_kbytes自动计算最低保留空闲内存(KB)
vm.overcommit_memory00=启发式,1=总是允许,2=限制commit
vm.zone_reclaim_mode00=不从其他zone回收,1=优先本地zone回收

八、总结

Linux 内核内存管理是一个庞大而精密的系统,从底层的 Buddy 页面分配,到 Slab 小对象管理,再到虚拟内存映射与页面回收,每一层都经过精心设计与持续优化。理解这些机制,不仅能帮助我们在系统出现内存问题时精准定位,更能指导我们在应用层做出更合理的内存使用策略。

在实际调优中,建议遵循"观察-分析-调整-验证"的循环,善用 vmstat、sar、slabtop、numastat 等工具,结合应用场景制定针对性的内存策略。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部