尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

深入理解Linux内核--内存架构,页框分配器,性能优化

深入理解Linux内核--内存架构,页框分配器,性能优化 1.页框1.1.NUMA 节点与物理内存组织1.1.1.NUMA 架构在 NUMANon-Uniform Memory Access系统中CPU 和内存被划分为多个节点Node。每个节点有自己的本地内存访问本地内存比访问远程节点内存快得多。内核用 struct pglist_data简称 pgdat表示一个 NUMA 节点// include/linux/mmzone.htypedefstructpglist_data{structzonenode_zones[MAX_NR_ZONES];// 该节点包含的内存区域structzonelistnode_zonelists[MAX_ZONELISTS];// 分配时的 fallback 列表unsignedlongnode_start_pfn;// 节点起始页框号unsignedlongnode_present_pages;// 实际存在的页数unsignedlongnode_spanned_pages;// 包含空洞的总跨度intnode_id;// NUMA 节点 IDwait_queue_head_t kswapd_wait;// kswapd 等待队列intkswapd_order;// kswapd 当前回收的 orderenumzone_typekswapd_highest_zoneidx;// kswapd 回收的最高 zoneatomic_t kswapd_failures;// 连续回收失败次数// ...}pg_data_t;每个节点包含若干 Zone内存区域分配内存时优先从本地节点的 Zone 分配本地不足时才 fallback 到远程节点。1.2.内存区域Zone—— 不同性质的物理内存1.2.1.Zone 类型Linux 将物理内存按地址范围和硬件限制划分为不同性质的 ZoneZone 类型说明典型范围ZONE_DMA用于 legacy ISA 设备的 DMA必须位于物理内存低 16MB 内 16MBZONE_DMA3232位设备可 DMA 的内存x86-64 上通常 4GB 4GBZONE_NORMAL可直接内核映射的内存线性映射区因架构而异ZONE_HIGHMEM32位系统中内核无法直接线性映射的内存需临时映射 896MB (x86)ZONE_MOVABLE用于可移动内存如 CMA、内存热插拔预留动态ZONE_DEVICE用于设备内存如 PMEM、GPU 显存设备特定在 64 位系统上通常只有 ZONE_DMA32 和 ZONE_NORMAL。1.2.2.Zone 的核心结构structzone{unsignedlongwatermark[NR_WMARK];// min/low/high/promo 水位线unsignedlongnr_reserved_highatomic;// 高阶原子分配预留structper_cpu_pages__percpu*per_cpu_pageset;// 每CPU缓存structfree_areafree_area[NR_PAGE_ORDERS];// 伙伴系统空闲链表unsignedlongzone_start_pfn;unsignedlongmanaged_pages;// 伙伴系统管理的页数unsignedlongpresent_pages;unsignedlongspanned_pages;constchar*name;// ...};1.3.伙伴系统Buddy System—— 页框分配核心1.3.1.基本思想伙伴系统将空闲页框按 2^order 个连续页 的块进行管理order 从 0 到 10对应 1~1024 页。order0:1page(4KB)order1:2pages(8KB)order2:4pages(16KB)...order10:1024pages(4MB)1.3.2.核心数据结构structfree_area{structlist_headfree_list[MIGRATE_TYPES];// 按迁移类型分链表unsignedlongnr_free;// 该 order 空闲块数};1.3.3.迁移类型Migrate Types—— 反碎片机制内核将页按可移动性分类避免不可移动页夹杂在可移动页中间导致碎片迁移类型用途MIGRATE_UNMOVABLE内核分配如 kmalloc 大对象不可迁移MIGRATE_MOVABLE用户态匿名页/文件页可迁移/交换MIGRATE_RECLAIMABLE可回收缓存如 slab、文件页缓存MIGRATE_HIGHATOMIC高优先级原子分配预留MIGRATE_CMA连续内存分配器区域MIGRATE_ISOLATE隔离中的页迁移/热插拔分配时优先从对应迁移类型的链表中取页该类型不足时可以steal降级其他类型的页块。1.3.4.分配与释放操作分配找到满足要求的最小 order 的空闲块如果 order 过大则分裂split为更小的块返回所需部分剩余部分挂到对应 order 的空闲链表。释放检查相邻页框是否为空闲且同 order若是则合并coalesce为更大的块递归向上合并。1.4.每 CPU 页框缓存Per-CPU Page Cache, PCP1.4.1.为什么需要 PCP伙伴系统的全局链表需要加锁zone-lock频繁的单页分配会导致锁竞争。PCP 为每个 CPU 维护一个本地缓存批量从伙伴系统取页/归还页大幅减少锁竞争。1.4.2.PCP 结构structper_cpu_pages{intcount;// 当前缓存中的页数inthigh;// 缓存上限超过则归还一批到伙伴系统intbatch;// 每次批量取/还的数量structlist_headlists[NR_PCP_LISTS];// 热页/冷页链表};1.4.3.热页 vs 冷页热页Hot Pages最近被使用过CPU 缓存中可能还有数据适合快速重用。冷页Cold Pages刚从伙伴系统取来或准备归还CPU 缓存中无有效数据适合 DMA 等场景。分配时优先从 PCP 的热页链表取order ≤ 3 的分配通常走 PCP 快速路径。1.5.水位线Watermarks与回收机制1.5.1.四条水位线每个 Zone 有四条水位线控制内存回收行为enumzone_watermarks{WMARK_MIN,// 最低水位低于此触发直接回收/Direct ReclaimWMARK_LOW,// 低水位低于此唤醒 kswapd 后台回收WMARK_HIGH,// 高水位kswapd 回收的目标WMARK_PROMO,// 用于分层内存CXL/NUMA页提升NR_WMARK};1.5.2.水位线计算水位线由 vm.min_free_kbytes 和 vm.watermark_scale_factor 推导watermark[min]min_free_kbytes 按 zone 比例分摊 watermark[low]min*(5/4)// 约 1.25x minwatermark[high]min*(3/2)// 约 1.5x min1.5.3.三级回收策略┌─────────────────────────────────────────────────────────────┐ │ Freehigh │ 健康状态无需回收kswapd 休眠 │ ├─────────────────────────────────────────────────────────────┤ │ lowFreehigh │ 压力初现唤醒 kswapd 后台异步回收 │ ├─────────────────────────────────────────────────────────────┤ │ minFreelow │ 压力较大分配进程可能进入 Direct │ │ │ Reclaim同步回收阻塞当前进程 │ ├─────────────────────────────────────────────────────────────┤ │ Freemin │ 极度紧张原子分配可能失败持续 │ │ │ Direct Reclaim 后可能触发 OOM Killer │ └─────────────────────────────────────────────────────────────┘1.5.4.kswapd 与 Direct Reclaimkswapd每个 NUMA 节点的后台内核线程。当 free pages low 时唤醒扫描 LRU 链表回收页直到达到 high 水位。回收内容包括干净文件页直接丢弃脏文件页写回后丢弃匿名页换出到 swap如有Slab 缓存通过 shrinker 收缩Direct Reclaim当 free pages min 时当前分配进程自己执行回收同步阻塞导致延迟抖动。若回收失败进入 OOM。1.6.完整页框分配流程1.6.1.入口函数structpage*alloc_pages(gfp_t gfp,unsignedintorder)└──__alloc_pages(gfp,order,preferred_nid,nodemask)└──__alloc_pages_nodemask(gfp,order,nid,nodemask)└──__alloc_pages(gfp,order,nid,nodemask)├──get_page_from_freelist()// Fast Path└──__alloc_pages_slowpath()// Slow Path1.6.2.Fast Path快速路径1.根据 gfp 标志确定首选 Zone 和 fallback 顺序2.检查目标 Zone 的水位是否满足(zone_watermark_ok)3.若满足 a.order0且3走rmqueue_pcplist()从 PCP 缓存取页 b.否则走__rmqueue()从伙伴系统 free_area 取页-__rmqueue_smallest()从低 order 到高 order 遍历-找到后del_page_from_free_list()必要时expand()分裂4.返回 page1.6.3.Slow Path慢速路径当 Fast Path 失败所有 Zone 水位不足时进入1.唤醒 kswapd如果未唤醒2.尝试直接回收Direct Reclaim-shrink_node()扫描 LRU-shrink_slab()收缩缓存-可能触发 compaction内存规整以获取连续页3.再次尝试get_page_from_freelist()4.若仍失败考虑 OOM Killer除非 __GFP_RETRY_MAYFAIL 等标志1.6.4.分配标志GFP对路径的影响GFP 标志行为GFP_ATOMIC不允许睡眠不走 Direct Reclaim可能失败GFP_KERNEL标准路径允许 Direct Reclaim 和 IOGFP_HIGHUSER用户态分配优先从 ZONE_HIGHMEMGFP_DMA/GFP_DMA32限制在对应 Zone__GFP_RETRY_MAYFAIL尽力回收但不触发 OOM__GFP_NOFAIL不允许失败极端情况下可能死等1.7.性能优化与调优1.7.1.监控与诊断# 查看伙伴系统状态各 order 空闲块数 cat/proc/buddyinfo # 查看 Zone 水位线和统计 cat/proc/zoneinfo|grep-Epages free|min|low|high|managed# 查看迁移类型分布 cat/proc/pagetypeinfo # 查看回收统计 cat/proc/vmstat|grep-Epgscan|pgsteal|allocstall|oom_kill1.7.2.关键调优参数参数作用建议vm.min_free_kbytes控制 min 水位线基准影响三级回收触发时机内存大时可适当增大如 64MB~128MB避免频繁 Direct Reclaimvm.watermark_scale_factor控制 low/high 与 min 的间距默认 10即 0.1%内存压力大时增大让 kswapd 更早介入vm.swappiness控制文件页回收 vs 匿名页换出的倾向0-200数据库等延迟敏感场景设为 10-30需要积极换出可设 100MGLRUvm.zone_reclaim_modeNUMA 节点内存不足时是否回收本地节点本地内存远快于远程时设为 1vm.numa_balancing自动 NUMA 页迁移平衡大多数场景保持开启11.7.3.避免 Direct Reclaim 延迟Direct Reclaim 是性能杀手会导致分配进程同步阻塞。优化方向保证 kswapd 能提前工作增大 min_free_kbytes 和 watermark_scale_factor确保 free 降到 low 之前 kswapd 已被唤醒。使用 Swap即使少量 swap 也能让匿名页有回收出路避免 OOM。内存 Cgroup 隔离限制单个容器/服务的内存上限避免其耗尽系统内存导致全局 Direct Reclaim。调整 oom_score_adj保护关键进程不被 OOM Kill。使用 MADV_HUGEPAGE / THP减少高阶分配频率降低伙伴系统分裂压力和碎片化。1.7.4NUMA 优化numactl / libnuma绑定进程到特定节点减少跨节点访问。自动 NUMA 平衡numa_balancing1内核自动将页迁移到访问它的 CPU 所在节点。分层内存Memory Tiering利用 WMARK_PROMO 和 CXL/PMEM 作为慢速层热页保留在 DRAM。1.7.5.碎片化治理内存规整Compaction/proc/sys/vm/compact_memory 手动触发或依赖 kcompactd 后台规整。CMAContiguous Memory Allocator预留一块可移动内存区域供驱动申请连续物理内存。避免长期持有不可移动页如减少内核大对象 kmalloc改用 vmalloc虚拟连续但物理不连续。1.8.总结图┌──────────────────────────────────────────────────────────────┐ │ 用户/内核内存请求 │ │alloc_pages(gfp,order)│ └──────────────────────────┬───────────────────────────────────┘ ▼ ┌──────────────────────────────────────────────────────────────┐ │1.确定 NUMA 节点和 Zone 优先级本地优先 → fallback │ └──────────────────────────┬───────────────────────────────────┘ ▼ ┌──────────────────────────────────────────────────────────────┐ │2.FAST PATH:get_page_from_freelist()│ │ ├── 检查zone_watermark_ok()│ │ ├── order0~3→ PCP 缓存rmqueue_pcplist │ │ └── 高 order → 伙伴系统__rmqueue → expand │ └──────────────────────────┬───────────────────────────────────┘ │ 失败 ▼ ┌──────────────────────────────────────────────────────────────┐ │3.SLOW PATH:__alloc_pages_slowpath()│ │ ├── 唤醒 kswapdfreelow │ │ ├── Direct Reclaimfreemin→ 扫描 LRU、shrink slab │ │ ├── Compaction内存规整 │ │ └── 重试分配 → 仍失败 → OOM Killer │ └──────────────────────────────────────────────────────────────┘
返回列表