ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

一篇文章读懂 openEuler OBMM:透明内存访问与 Remote NUMA 节点管理的底层原理

一篇文章读懂 openEuler OBMM:透明内存访问与 Remote NUMA 节点管理的底层原理 一篇文章读懂 openEuler OBMM透明内存访问与 Remote NUMA 节点管理的底层原理【免费下载链接】obmmUser-space ownership-based memory management component - the underlying infrastructure for heterogeneous memory pooling.项目地址: https://gitcode.com/openeuler/obmmOBMMOwnership Based Memory Management基于所有权的内存管理是 openEuler 面向超节点与异构内存池化的底层内存管理组件核心能力是透明内存访问与 Remote NUMA 节点管理让应用无需改造就能使用远端内存从而突破单机物理内存的上限。内存不够用了除了加内存条还能怎么办试想一个再常见不过的场景跑分析型数据库的服务器256GB 内存已经用满数据集还在增长。加内存条要停机换硬件换大内存机器要整机迁移成本都不小。可如果旁边正好有一台内存大量闲置的机器呢能不能把它的内存借过来当成自己机器的一部分来用而且应用完全感知不到这就是 openEuler OBMM 要解决的问题。它不是给内存条扩容而是把整个互连网络里的空闲内存组织成一个可动态伸缩的内存池。底层依赖 Unified Bus、CXL 这类 sig-Long 总线技术上层则把跨节点用内存这件事包装得和用本地内存一样简单。OBMM 由两部分组成内核模块obmm.ko负责打通硬件通路、管理内存设备用户态库libobmm.so提供obmm_export()、obmm_import()等一系列接口。应用真正要面对的是用户态 API门槛并不高。一句话说清 OBMM 到底解决了什么传统方案想用远端内存通常要走 RDMA 这类显式通信开发者需要改写代码、引入新的通信库改造成本很高也很难做到对既有应用透明。OBMM 的本质区别在于两个包装透明内存访问打通硬件通路后应用用普通的load/store指令就能读写远端内存不需要任何特殊 APIRemote NUMA 节点管理把远端内存作为新的 NUMA 节点上线交给内核的既有内存调度机制去分配应用零改造就能用上扩展出来的容量。换句话说前者解决怎么访问后者解决怎么分配两者叠加才实现了真正意义上的内存池化。拆开数据通路一次远端访问是如何伪装成本地访问的要理解透明我们先看一次访存请求在硬件里经历了什么。OBMM 会依次配置三块地址翻译组件MMU 页表把虚拟地址VA翻译成本机物理地址PAUB memory decoder 翻译表把物理地址翻译成 UB 总线的统一地址UBA这张表由高可信硬件配置OBMM 以其给出的 PA 为起点去配置其他组件UMMU 翻译表在远端把 UBA 还原成远端物理地址。上图画出了完整数据流本地 CPU 发出load指令 → 本机 MMU 把 VA 转成 PA → UB decoder 把 PA 转成 UBA → 请求经 UB interconnect 到达远端 → 远端的 UMMU 把 UBA 还原成远端 PA → 最终访问远端 DRAM。整个过程对应用完全透明它只觉得自己在读本地内存读写的却是另一台机器上的数据。为什么软件要接管一致性读懂所有权机制的由来既然有了硬件通路为什么还需要所有权这个概念这要从总线技术现状说起。Unified Bus、CXL 这类高速互连目前对跨节点数据一致性的支持存在明显限制。如果多个节点同时写同一块内存就会出现数据竞争和结果不一致。硬件层面暂时做不到就只能靠软件来兜底。OBMM 的思路是任意时刻一块共享内存只允许一个节点拥有写权限。它通过obmm_set_ownership()这个接口管理权限状态把谁在写这件事显式化。这有点像图书馆借书——同一本书可以有很多人同时阅读共享只读但只有借走的那一个人能在上面写批注独占写权。这个机制保证了在缺乏硬件一致性保证的环境中跨节点共享内存依然是安全、可控的。从导出到回收远端内存的完整生命周期使用远端内存本质上是一个借—用—还的过程官方文档给出了必须遵守的控制面时序提供方obmm_export()申请并导出一段内存此时内存已清零不会泄漏历史数据使用方obmm_import()引入远端内存生成字符设备/dev/obmm_shmdev${mem_id}数据访问应用按需读写使用方obmm_unimport()解除引入提供方obmm_unexport()回收内存。/* 使用方引入一段远端内存并映射访问 */ struct obmm_mem_desc desc { .addr pa, /* 远端内存对应的物理地址 */ .length size, .scna scna, }; memcpy(desc.seid, seid, 16); id obmm_import(desc, OBMM_IMPORT_FLAG_ALLOW_MMAP, 0, NULL);需要注意的是所有接口都受OBMM 基础粒度约束。基础粒度定义为提供方线性映射页大小、进程页大小、缓存更新粒度的最小公倍数典型值为 2MB4K 页场景。地址和长度都要按这个粒度对齐否则接口会直接返回非法参数错误。借用还是共享两种使用姿势怎么选obmm_import()时有两个互斥的标志位决定了内存引入后的软件接口形态使用模型标志位特点管理手段借用OBMM_IMPORT_FLAG_NUMA_REMOTE内存以远端 NUMA 节点上线独占访问必须 cacheablenumactl、mbind、move_pages等内核既有机制共享OBMM_IMPORT_FLAG_ALLOW_MMAP通过 mmap 字符设备映射多方可以交替使用mmap 进程页表借用模型适合把远端内存当本地内存用的容量扩展场景交给内核调度即可共享模型适合需要精细控制访问方、做数据共享的场景但对一致性要格外小心。这里有一个关键的粒度差异借用模型下内存热插的粒度是 128MB内核页 4K/16K或 512MB64K而共享模型的进程页表粒度只有 4K。选错模式性能和功能都会受影响。三步完成预导入配置把热路径延迟压下来以 NUMA 方式引入远端内存时创建 NUMA 节点是个较重的操作耗时会落在关键路径上。预导入preimport的思路是把建节点这件事提前到内存真正上线之前而且不依赖内存导出时的完整信息所以甚至可以在导出之前完成。/* 第一步预导入提前创建 remote NUMA 节点 */ struct obmm_preimport_info info { .pa pa, .length size, .base_dist 0, .numa_id -1, .priv_len 0, }; obmm_preimport(info, 0); /* 系统自动分配 numa_id 并回填 */ /* 第二步实际上线将内存注入预导入节点 */ desc.addr pa; desc.length size; obmm_import(desc, OBMM_IMPORT_FLAG_NUMA_REMOTE | OBMM_IMPORT_FLAG_PREIMPORT, 0, NULL); /* 第三步回收 */ obmm_unimport(id, 0); obmm_unpreimport(info, 0);预导入用物理地址段pa, length作为匹配索引实际obmm_import()的地址段必须是预导入地址段的子集且各预导入段之间不能重叠。配套的base_dist参数控制 NUMA 距离等于 0 表示新节点与所有本地节点等距距离 100在 11255 范围内则根据基准 NUMA 节点计算相对距离。建议始终传入相同的值避免多次上线时 NUMA distance 被反复重算。申请内存时从哪进货三种内存来源对比UB memory 对提供方内存的连续性和缓存属性有要求一般要由 OBMM 统一分配。内核模块的mempool_allocator参数决定内存来源运行期不可修改内存来源分配粒度适用场景关键限制hugetlb_pmd2M4K 页/ 512M64K 页高性能计算需预留大页内存要求 pmd_mapping100%hugetlb_pud1G4K 页大数据、超大内存需预留大页粒度粗1G 起步buddy_highmem可配置≥2M 的 2 的幂通用场景从 buddy 系统动态组合不保证全部申请成功为了加速分配OBMM 还维护了一个可选的缓冲内存池大小由mempool_size参数控制默认 1G状态可以在/sys/kernel/obmm_mempool/下查看。导出内存时优先从池中取系统内存紧张触发 OOM 时内存池会主动把内存还给系统等mempool_refill_timeout毫秒后再尝试重新填充。快速上手与验证从内核参数到观测命令想跑起来需要先满足内核侧的前置条件开启 NUMA 上线功能配置内核参数numa_remote想用预上线则配置numa_remotepreonline配置页表映射使用buddy_highmem需要pmd_mapping使用hugetlb_pmd需要pmd_mapping100%64K 页场景改用rodatafull确保依赖的ubus、ubcore、hisi_ummu_core等模块正常加载。安装完成后可以通过 sysfs 直接观察效果# 查看某段内存的总大小 cat /sys/devices/obmm/obmm_shmdev${mem_id}/size # 查看远端内存上线到了哪个 NUMA 节点 cat /sys/devices/obmm/obmm_shmdev${mem_id}/import_info/numa_id # 查看该内存是否走了预导入加速 cat /sys/devices/obmm/obmm_shmdev${mem_id}/import_info/preimport # 查看所有预导入地址段 cat /proc/obmm/preimport_info # 查看内存池状态 cat /sys/kernel/obmm_mempool/obmm-0/available_cleared想动手实验可以直接 clone 仓库https://gitcode.com/openeuler/obmm用户态 API 全部集中在 src/libobmm/libobmm.h 中几十个接口一目了然。常见性能瓶颈排查与避坑清单结合社区文档和实际部署经验以下几个点最容易踩坑对齐是第一铁律地址和长度必须按 OBMM 基础粒度典型 2M对齐NUMA_REMOTE 模式下还要求按 128M/512M 对齐否则直接返回EINVAL控制面时序不可乱必须严格按 export → import → 访问 → unimport → unexport 的顺序执行违反可能导致进程崩溃、数据不一致甚至芯片异常共享 cacheable 内存务必维护权限只有 cacheable mmap 的共享模型才涉及一致性切换读写权限时要用obmm_set_ownership()否则多 host 并发写会造成数据竞争预导入索引要唯一各预导入地址段不能重叠也不能与普通导入的地址段重叠两端内核编译选项保持一致提供方和使用方的内核核心编译选项不同会导致粒度约束出现误拦截或漏拦截NUMA distance 要稳定非预导入模式下多次增量上线会用最后一次的 base_dist 覆写距离建议应用始终传相同的值。性能边界与未来方向OBMM 还能走多远任何方案都有边界OBMM 也不例外。它的性能受制于软件一致性管理的开销写权限切换会触发缓存回刷与失效NUMA 距离上限是 255且目前定位在单机内管理远端内存。这些约束决定了它更适合内存池化、透明扩容这类场景而不是替代低延迟的本地访问。面向未来OBMM 的演进方向相当明确AI 驱动的智能内存调度与迁移、DDR/HBM/持久内存的异构统一管理、边缘计算场景下的内存共享以及更强的内存隔离与安全保护。2025 年 11 月项目已在社区开源并随 openEuler 24.03 SP3 作为核心组件发布生态正在快速成型。结尾它对你意味着什么对开发者来说OBMM 的价值是实打实的应用一行代码不改就能把远端闲置内存变成自己的内存容量。对运维人员来说它把加内存从一次停机变更变成了随时可以弹性伸缩的软操作。想深入下去建议按这个路径走先读 doc/libobmm.md 和 doc/obmm.md 建立整体认知再看 doc/obmm_export.md、doc/obmm_import.md、doc/obmm_preimport.md 三个核心接口文档最后打开 src/libobmm/ 的源码对照阅读。如果你手头正好有支持 Unified Bus 或 CXL 的硬件不妨克隆仓库跑一个最小的导出-引入示例亲自感受一下内存从远端来的体验。【免费下载链接】obmmUser-space ownership-based memory management component - the underlying infrastructure for heterogeneous memory pooling.项目地址: https://gitcode.com/openeuler/obmm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表