第 1 章:kswapd 的作用
1.1 背景
Linux 内存管理设计中,物理内存由页(page)为单位管理。系统运行过程中,应用程序会不断申请内存,导致可用的页会逐渐的减少。当可用页不足时,如果没有回收机制,内存分配将失败,可能触发 OOM(Out Of Memory)杀手,这也是我们设备常遇到的问题,我经常遇到,不知道朋友们有遇到过没有。
kswapd 是为了解决这种长期后台内存压力问题。它是内核在每个内存节点(NUMA Node)中启动的低优先级守护线程,主要负责扫描内存页,回收不活跃页以维持系统稳定性。kswapd 背后的理念是“预防式回收”:当内存压力尚未达到紧急状态时,通过后台回收释放页,保证应用程序在分配内存时不用等待。
kswapd 的工作涉及 LRU 链表管理、脏页写回、Page Cache 回收和匿名页回收。内核将内存页分为 Active/Inactive 链表,通过 kswapd 定期扫描 Inactive 链表,将冷页回收或写回磁盘;同时结合 direct reclaim 机制处理急需内存分配的场景。kswapd 与页面回收机制紧密结合,是 Linux 内存管理的核心守护线程之一。
1.2 kswapd 与直接回收(Direct Reclaim)关系
Linux 内核存在两种主要回收策略:后台回收(kswapd)和直接回收(direct reclaim)。后台回收由 kswapd 触发,低优先级运行,周期性扫描内存并回收冷页;直接回收由触发内存分配失败的进程主动执行,优先级高,保证分配请求能够及时满足。
这两种机制形成互补:kswapd 预回收,避免内存耗尽;direct reclaim 补救,保证紧急分配。内核通过 mm_flags 和 vm_pressure 指标监控内存压力,并根据压力等级决定回收策略。结合 LRU 链表管理,kswapd 能够在后台平滑释放 Page Cache、匿名页和 slab 缓存,实现内存可用性与 I/O 性能的平衡。
kswapd 工作流程:
┌─────────────┐│ 内存压力监控 │└───────┬─────┘ │ ▼┌─────────────┐│ 启动 kswapd │└───────┬─────┘ │ 扫描节点 LRU 链表 ▼┌─────────────┐│ 回收冷页/脏页│└───────┬─────┘ │ 写回磁盘/释放内存 ▼┌─────────────┐│ 可用内存增加 │└─────────────┘
第 2 章:内存压力指标与 kswapd 启动条件2.1 内存压力检测机制
Linux 内核通过多个指标检测内存压力。核心指标包括:
free pages:系统当前可用页数量。
watermark(水位):内核为不同优先级内存分配设定的阈值,分为 min、low、high 三档,这个在/proc下可以查看的。
vm_pressure:根据内存消耗速率和剩余页数量动态计算压力等级。
kswapd 启动条件通常为可用内存低于 low watermark,但高于 min,此时无需阻塞应用程序,可以安全后台回收。若内存低于 min,则触发 direct reclaim,由申请内存的进程主动回收页。
这种多层次压力检测实现了内存管理的柔性调度:后台守护线程预防内存耗尽,而应用进程在紧急情况下仍能及时回收所需内存。
2.2 NUMA 与节点级 kswapd
在 NUMA(Non-Uniform Memory Access)架构中,每个节点拥有独立内存。Linux 为每个节点启动独立 kswapd,扫描本节点的 LRU 链表并回收本地页。跨节点访问可能引入远程内存访问延迟,因此内核优先回收本地节点冷页。
节点级 kswapd 避免了远程内存访问对性能的负面影响。kswapd 会结合内存分配策略(如 ZONE_NORMAL、ZONE_HIGHMEM)按优先级扫描冷页,同时考虑脏页写回批量大小和回收速率,确保系统整体平衡。
NUMA 节点回收流程:
NUMA Node 0 NUMA Node 1┌─────────────┐ ┌─────────────┐│ kswapd 0 │ │ kswapd 1 │└───────┬─────┘ └───────┬─────┘ │ │ ▼ ▼ ┌─────────────┐ ┌─────────────┐ │ 扫描本地 LRU │ │ 扫描本地 LRU │ └───────┬─────┘ └───────┬─────┘ │ │ ▼ ▼ 回收冷页/写回脏页 回收冷页/写回脏页
第 3 章:LRU 链表与内存回收逻辑
3.1 Active 与 Inactive 链表
Linux 将内存页分为 Active 和 Inactive 链表。Active 链表包含近期访问频繁的页,Inactive 链表包含冷页。kswapd 的扫描重点在 Inactive 链表,通过以下步骤回收:
遍历 Inactive 链表页。
对于匿名页(Heap/Stack),尝试 swap-out。
对于文件页(Page Cache),检查是否脏页,必要时写回磁盘。
将回收的页释放到 buddy system 可用页链表。
这种设计使系统在内存压力下能够优先保留热数据,回收冷数据,提高内存分配效率,同时减少磁盘 I/O 冲击。
3.2 回收策略与脏页处理
脏页写回是 kswapd 回收的重要环节。kswapd 会批量写回脏页以减少随机 I/O,并根据 dirty_ratio 和 dirty_background_ratio 调整回写阈值,防止写回操作过于激进导致 I/O 突发。
kswapd 的回收策略需要平衡内存释放速度与 I/O 压力。如果回收过慢,应用分配可能阻塞;如果过快,磁盘 I/O 突发,影响整体系统性能。内核通过 LRU + 批量写回 + 优先级扫描形成高效的后台回收机制。
kswapd LRU 扫描流程:
Inactive LRU Chain┌─────────────┐│ 页1(anon) ││ 页2(file) ││ 页3(file,dirty) │└───────┬─────┘ │ ▼┌─────────────┐│ 区分类型 │└───────┬─────┘ │ ┌──────┴──────┐ ▼ ▼匿名页 文件页swap-out 写回磁盘
第 4 章:kswapd 与 Page Cache 协同
4.1 Page Cache 冷页回收
Page Cache 是 Linux 内核缓存文件页的核心。kswapd 在回收 Inactive 链表时,首先扫描 Page Cache 冷页,将长时间未访问的缓存页释放到 buddy system。理论上,这种回收不会影响应用运行,因为冷页通常可从磁盘重新加载。
kswapd 批量释放 Page Cache 页,有效降低内存压力,提高可用页数量。配合 LRU 算法和工作集理论,kswapd 能够在后台平滑回收缓存,避免突发 I/O。
4.2 脏页回写机制
对于脏页,kswapd 会按照阈值和批量写回策略执行写回操作。写回流程包括:
将脏页放入 writeback 队列。
调用 filesystem flush 回写磁盘。
页写回完成后释放到 buddy system。
这种机制保证了数据一致性和性能平衡,同时允许系统在后台维持高命中 Page Cache,提高应用访问效率。
脏页写回:
┌─────────────┐│ 脏页标记 │└───────┬─────┘ ▼┌─────────────┐│ 写入 writeback 队列 │└───────┬─────┘ ▼┌─────────────┐│ 批量写回磁盘 │└───────┬─────┘ ▼┌─────────────┐│ 页释放到 buddy │└─────────────┘
第 5 章:kswapd 与 Direct Reclaim 协作机制
5.1 Direct Reclaim 触发条件与流程
当应用程序分配内存时,如果可用页低于 min watermark,将触发 Direct Reclaim,由请求内存的进程主动执行回收,而非依赖后台 kswapd。Direct Reclaim 的特点是高优先级、实时性强,确保进程可以及时获得内存。
Direct Reclaim 与 kswapd 协作形成双层防护机制:kswapd 预回收,避免内存紧张;Direct Reclaim 补救紧急情况。Direct Reclaim 回收逻辑与 kswapd 类似,也扫描 Active/Inactive LRU 链表,区分匿名页、文件页,并处理脏页写回。但不同点在于,Direct Reclaim 会阻塞调用线程,直至释放足够页。
流程:
┌───────────────┐│ 进程申请内存 │└───────┬───────┘ ▼┌───────────────┐│ 检查 free pages│└───────┬───────┘ 可用页 < min ? │ ┌────┴─────┐ ▼ ▼触发 Direct Reclaim 不触发 │ ▼扫描 Inactive LRU回收冷页/写回脏页释放到 buddy system │ ▼分配内存成功
这种协作机制保证了 Linux 系统在高负载下既不会频繁阻塞进程,也不会过早触发 OOM。通过合理的 watermark 设置和 Direct Reclaim 机制,系统实现了内存回收的“预防+应急”双模式策略。
5.2 kswapd 与 Direct Reclaim 优化协作
kswapd 和 Direct Reclaim 在实际执行中需要协调:
压力等级区分:kswapd 在低于 low watermark 时预回收,避免触发直接回收;Direct Reclaim 仅在可用内存低于 min watermark 时触发。
回收目标差异:kswapd 优先回收冷页和 Page Cache,减少对进程运行的干扰;Direct Reclaim 优先回收匿名页和热页,保证进程及时分配到内存。
批量写回优化:kswapd 批量写回脏页,降低 I/O 并发;Direct Reclaim 可能触发同步写回,保证内存分配成功。
理论上,这种协作机制使 Linux 在高并发、大规模内存申请场景下保持稳定。结合 LRU 链表和 NUMA 节点优先级策略,kswapd 与 Direct Reclaim 能够平滑管理内存压力,实现高性能与低延迟的平衡。
协作流程:
┌───────────────┐│ kswapd 扫描后台│└───────┬───────┘ ▼ 释放冷页/脏页 │ ▼┌───────────────┐│ Direct Reclaim│└───────┬───────┘ ▼阻塞进程释放页 │ ▼分配内存成功
第 6 章:kswapd 调优策略与性能优化
6.1 内核参数调优
kswapd 的性能调优主要通过内核参数控制:
watermark_water:设置 min/low/high 内存水位,影响 kswapd 和 Direct Reclaim 的触发条件。
kswapd_sleep_millisecs:控制 kswapd 扫描间隔,调整后台回收频率。
dirty_ratio / dirty_background_ratio:控制脏页最大占比和后台写回阈值,避免写回突发导致 I/O 压力。
vfs_cache_pressure:影响 dentry、inode 缓存回收倾向,间接影响 kswapd 回收目标。
理论上,通过调整这些参数,可以实现不同场景下的内存回收优化:高性能服务器倾向保留更多缓存,减少 I/O;嵌入式或内存紧张环境倾向更激进的回收策略,保证系统稳定性。
6.2 高负载与 NUMA 优化
在 NUMA 系统中,kswapd 的优化策略包括:
本地回收优先:kswapd 优先扫描本节点内存,减少远程访问延迟。
节点平衡扫描:若本地节点无法释放足够页,可考虑跨节点回收,但会带来访问延迟。
脏页回写批量调节:在多节点场景下,通过 writeback batching 降低 I/O 并发和延迟。
理论上,这些优化确保了 NUMA 系统在大规模内存使用、并发分配和高 I/O 压力下,kswapd 能够稳定释放内存,防止某节点内存耗尽引发整体性能下降。
NUMA 优化流程:
┌─────────────┐│ kswapd Node0 │└─────┬───────┘ ▼扫描本地 LRU │ ▼本地冷页不足? │ ▼跨节点扫描 │ ▼释放页到 buddy system
7.1 高 I/O 系统中的 kswapd 行为
在数据库、虚拟化、日志密集型系统中,kswapd 扫描频率和回收速率直接影响性能。例如:
大量 Page Cache 冷页会被 kswapd 回收,以便应用分配匿名页。
高并发写入会触发脏页批量写回,减少同步写压力。
NUMA 节点负载不均时,kswapd 本地回收优先策略可降低远程访问延迟。
理解 kswapd 扫描逻辑和回收优先级,有助于调整 watermark、水位和 writeback 参数,从而在高 I/O 场景下维持系统稳定。
7.2 实践
监控指标:使用 vmstat、/proc/meminfo、slabtop 等观察 free pages、LRU 长度、kswapd wakeups。
合理水位设置:确保 low watermark 高于工作集峰值,避免频繁触发 Direct Reclaim。
脏页控制:调整 dirty_ratio 和 dirty_background_ratio,避免突发写回影响 I/O。
NUMA 优化:在多节点系统,确保 kswapd 本地扫描优先,避免跨节点频繁访问。
kswapd 全流程 :
┌───────────────┐│ 内存压力监控 │└───────┬───────┘ ▼┌───────────────┐│ 启动 kswapd │└───────┬───────┘ ▼扫描 Inactive LRU │ ▼┌───────────────┐│ 区分页类型 │└───────┬───────┘ ┌────┴────┐ ▼ ▼匿名页 文件页swap-out 写回磁盘 │ │ └─────┬───┘ ▼释放页到 buddy system │ ▼满足内存请求 → 进程继续