从异构内存管理角度看 Linux MM 锁机制的进化史 —— 一把大锁到异构内存迁移
本文系统地梳理 Linux MM 子系统中与内存迁移相关的锁与同步机制的演进脉络:每把锁因何而生、解决了什么瓶颈、又带来什么新问题。贯穿全文的一条主线:锁粒度不断从一把大锁保护整块细化为多把小锁各管一小块;同时不断引入新机制,去同步 CPU 页表之外的观察者(设备/虚拟机)和迁移中的访问者。目录阶段一:一把大锁的时代阶段二:锁粒度细化阶段三:同步 CPU 之外的观察者阶段四:异构内存与设备迁移阶段五:缺页路径再提速一张演进全景图对迁移这件事的累积影响1. 阶段一:一把大锁的时代1.1mmap_sem—— 地址空间的全局读写锁最早,一个进程地址空间里几乎一切都由mm-mmap_sem(读写信号量)保护:VMA 树的查找与修改;缺页处理handle_mm_fault();页表遍历、get_user_pages();以及mmap/munmap/mprotect/mremap/brk等系统调用。优点:模型极简——“改地址空间相关的东西,先拿这把锁”。瓶颈:它是进程级的单锁。多线程程序里,一个线程缺页(读锁)会与另一个线程mmap(写锁)互斥;高并发缺页彼此也在读锁的 cache line 上颠簸。对数据库、JVM、大型 C 服务这类多线程 大量按需分页的负载,mmap_sem长期是头号扩展性瓶颈。演进动作:2020 年前后社区把mmap_sem更名为mmap_lock,并引入一整套封装 API(mmap_read_lock()/mmap_write_lock()/...)。更名本身不改变语义,但把所有加锁点收敛到统一入口,为后续把职责从这把大锁里拆出去铺路。1.2page_table_lock—— 每 mm 一把的页表锁页表项的修改最初由mm-page_table_lock(每个 mm 一把自旋锁)串行化。瓶颈:多核同时修改不同页表区域时,仍要争抢同一把锁。对大地址空间、多核并发缺页,这把锁的争抢很快显现。阶段一的共同问题:锁的作用域太大。保护范围与并发实体(线程/CPU)不匹配,天然限制扩展性。阶段二就是针对性地拆。2. 阶段二:锁粒度细化2.1 split PTL —— 把页表锁拆到每个页表页一把动机:page_table_lock太粗。做法:引入split page table lock——锁不再是每 mm 一把,而是挂在每个页表页(pmd/pte 页)的struct page上,每页一把。取锁接口:PTE 级用pte_offset_map_lock(mm, pmd, addr, ptl);PMD 级(THP)用pmd_lock(mm, pmd)。收益:修改不同页表页的 PTE 之间不再互斥,页表操作真正并行化。这就是基础篇里反复出现的PTL。后来还配合RCU 释放页表页,让无锁的页表读者(如 GUP-fast、lockless page fault 的部分阶段)能安全地遍历。关键遗产:“改一个 PTE 必须拿覆盖它的那把 PTL”成为全内核的硬约定。munmap 的zap_pte_range、mprotect 的change_pte_range、mremap 的move_ptes、迁移的 collect/finalize、rmap 的try_to_migrate_one——全都遵守它。这正是某条路径即使不持 mmap_lock,只要在 PTL 下改 PTE 就与其他改 PTE 者串行化的根基。2.2 object-based rmap —— 从物理页反查所有映射动机:早期没有高效的物理页 → 谁映射了它的反查。换出或迁移一个被多进程共享的页时,找齐所有 PTE 代价高昂,甚至只能扫全表。做法:引入基于对象的reverse mapping(rmap):匿名页通过anon_vma/anon_vma_chain把一个页关联到映射它的所有 VMA;文件页通过address_space的区间树。收益:rmap_walk()可以枚举一个 folio 的所有映射并逐一处理。这是后来try_to_migrate()能只拿着物理页、不知道任何虚拟地址就把该页从所有地址空间里解除映射的前提——也是migrate_device_*(按 PFN)整条路线成立的基础。2.3 page lock → folio lock —— 冻结单个物理页动机:需要一个对单页操作的序列化点,让迁移/回收/写回期间别人别插手。做法:用struct page的PG_locked位(lock_page/unlock_page)。近年folio 化改造后统一为folio lock(folio_lock/folio_trylock/folio_unlock),天然覆盖复合页(THP)。收益:迁移用它来冻结 folio——从装 migration entry 到 finalize 全程持锁;等在 migration entry 上的访问者,本质就是在等这把 folio lock(基础篇 §5.1 已核实等待对象正是PG_locked)。3. 阶段三:同步 CPU 之外的观察者前两阶段都在解决CPU 侧页表/物理页的并发。但现代系统里,对同一物理页持有映射的不只是 CPU。3.1 mmu_notifier —— 让 GPU/IOMMU/KVM 与 CPU 页表同步动机:KVM 的影子页表、GPU 的设备页表、IOMMU 的映射,都缓存了虚拟/设备地址 → 物理页的翻译。当 CPU 改动或迁移某页时,这些外部翻译必须先失效,否则设备会访问到已被搬走/释放的旧页,造成数据损坏或安全问题。做法:2008 年前后引入mmu_notifier,有两种形态。经典范围通知:mmu_notifier_invalidate_range_start()/end()成对包住页表修改,约定失效通知必须发生在改 PTE 之前(start)与之后(end)。interval notifier(较新):mmu_interval_notifier,驱动在一段 VA 上注册,该段内任何失效都会回调其.invalidate,GPU SVM(如 drm_gpusvm)用的就是它。事件类型enum mmu_notifier_event:同一套回调用事件类型区分语义,常见如下表。事件大意是否带pgmap_ownerMMU_NOTIFY_UNMAP地址被解除映射否MMU_NOTIFY_CLEAR通用清除/失效否MMU_NOTIFY_MIGRATE按地址范围迁移是MMU_NOTIFY_MIGRATE带owner的意义:让按 owner 过滤的驱动跳过对自己拥有、且本次不会真正迁移的 device-private 映射的失效,避免自我失效。是否利用这个过滤完全取决于驱动回调实现——这一点会直接影响后续具体路径分析的结论。3.2 migration entry —— 对 CPU 访问者的路障动机:迁移一个页需要时间窗口。窗口内如果有 CPU 访问该 VA,既不能丢访问,也不能让它读到搬到一半的页。做法:把 PTE 临时换成一种特殊swap entry(migration entry)。窗口内任何访问该 VA 会缺页进do_swap_page(),识别出 migration entry 后migration_entry_wait()阻塞在目标 folio 的PG_locked上,直到迁移完成解锁再重试。migration entry(路障) folio lock(等待对象) PTL(原子替换/移除)三者合起来,构成迁移窗口内对 CPU 侧访问的完整保护。4. 阶段四:异构内存与设备迁移4.1 HMM /ZONE_DEVICE/ device-private page动机:GPU/加速器有自己的高带宽显存。希望让这块显存能像普通内存一样参与进程地址空间(SVM/统一地址空间),按需在 CPU RAM 与设备显存之间迁移。做法:2017 年前后的HMM(Heterogeneous Memory Management)引入ZONE_DEVICE页与device-private页:设备显存以ZONE_DEVICE的struct page表示;迁到设备后,CPU 侧 PTE 变成device-private swap entry(非 present,但仍在 rmap 中、仍计入 mapcount);CPU 访问该 entry 会触发-migrate_to_ram回调,由驱动把页迁回 RAM。4.2 两个迁移入口:按地址 vs 按 PFN在上述积木之上,内核提供了两套面向设备的迁移 API,分别服务不同场景。(a)migrate_vma_*—— 按虚拟地址范围迁移。场景:CPU 缺页触发(migrate_to_ram)、或用户/驱动请求把一段 VA 迁到显存(migrate_to_devmem)。特征:有 VMA、持 mmap_lock(至少读),走walk_page_range收集 PTE,并发MMU_NOTIFY_MIGRATE(带pgmap_owner)。局限:单 VMA / 单 mm、按地址范围。(b)migrate_device_*—— 按物理 PFN 迁移。场景:驱动主动腾显存 / 设备 unbind / shrinker——驱动只知道要搬哪些物理 device PFN,不一定持有(也不想遍历)它们的虚拟映射,且这些页可能被多个进程共享。特征:不碰 VMA、不持 mmap_lock,靠rmap(try_to_migrate)找到并解除该页在所有地址空间中的映射,其 PTE 失效通知走MMU_NOTIFY_CLEAR(经try_to_migrate_one,不带 owner)。依赖:正是阶段二的rmapsplit PTLfolio lock让只拿物理页也能安全迁移成为可能。这两个入口的差异(是否要 VMA/mmap_lock、如何定位 PTE、发什么 mmu 事件),正是 drm_pagemap 里fault 路径 vs eviction 路径分歧的根源。5. 阶段五:缺页路径再提速5.1 per-VMA lock(SPF,Speculative Page Fault)动机:即便有了 split PTL,缺页仍要先拿进程级mmap_lock读锁,高并发缺页依旧在这把锁上排队。典型场景是多线程程序:线程 A 在缺页(需要mmap_lock读锁),线程 B 在mmap/brk(需要写锁),二者互斥;即使 A、B 操作的是完全不相干的两段 VMA,也被这把全局锁强行串行化。split PTL 解决的是改 PTE的并发,却没解决进入缺页处理前那道门槛的并发。核心思路:既然一次缺页通常只关心一个 VMA,那就把读锁下沉到VMA 粒度——只锁住命中的那个 VMA,而不是锁住整棵 VMA 树。做法:2023 年前后合入per-VMA lock。关键机制有三层。RCU 查找 VMA:缺页快路径用lock_vma_under_rcu(),在RCU 读侧临界区里(不持mmap_lock)从 maple tree 查到覆盖故障地址的 VMA。RCU 保证遍历期间 VMA 结构体不会被释放。seqcount 校验 VMA 读锁:找到候选 VMA 后,用vma_start_read()尝试拿该 VMA 的读锁。它基于seqcount:写者(改这个 VMA 的人)会通过vma_start_write()递增序列号;读者拿锁时比对序列号,若发现有写者正在改这个 VMA,就放弃快路径。成功拿到后,置标志FAULT_FLAG_VMA_LOCK进入handle_mm_fault(),全程不碰mmap_lock。失败即回退:任何一步不满足(VMA 没找到、正被写者修改、或落进尚未适配 per-VMA lock 的慢路径),就vma_end_read()释放 VMA 读锁,返回VM_FAULT_RETRY,由上层重新以传统mmap_read_lock()走一遍。回退保证了正确性:快路径只做能安全做的那部分,拿不准就退回老路。直觉:mmap_lock是整栋楼的大门钥匙,per-VMA lock 是每个房间自己的钥匙。改 A 房间不再挡住进 B 房间的人;但凡遇到需要动整栋楼结构(跨 VMA、合并/split)的操作,还是得回去拿大门钥匙。边界:哪些缺页不能在 per-VMA lock 下完成。per-VMA lock 是渐进式铺开的——先覆盖最常见、最独立的匿名页/文件页缺页,尚未适配的路径一律主动退回。device-private 的migrate_to_ram正是尚未适配的一员:}elseif(softleaf_is_device_private(entry)){if(vmf-flagsFAULT_FLAG_VMA_LOCK){/* migrate_to_ram is not yet ready to operate under VMA lock. */vma_end_read(vma);retVM_FAULT_RETRY;/* 回退到 mmap_read_lock 重试 */gotoout;}...}为什么migrate_to_ram目前坚持要mmap_lock:设备迁移回调(如migrate_vma_*)会跨越单个 PTE 的边界——它要walk_page_range遍历一段地址、收集多个 PTE、发MMU_NOTIFY_MIGRATE通知、可能触发 VMA 相关的分配与状态更新。这些动作依赖整个地址空间布局在此期间稳定,而 per-VMA lock 只锁住一个VMA、且语义上更弱,尚不足以支撑。于是内核选择保守退回:一旦发现是在FAULT_FLAG_VMA_LOCK下命中 device-private entry,立即放弃快路径。推论(对 GPU SVM 的直接影响):所以 GPU SVM 的 fault 回调真正执行时,一定在mmap_read_lock之下,而不可能在 per-VMA lock 下。这条退回逻辑正是fault 路径 VMA 稳定这一前提的制度保证——不是驱动自己去争取,而是内核在入口就替它把不安全的情形挡掉了(详见基础篇 §1.1)。未来演进方向:社区在逐步让更多路径VMA-lock 化。若某天migrate_to_ram也适配了 per-VMA lock,上面这段VM_FAULT_RETRY会被移除,fault 路径的锁前提也会随之改写——这也是为什么本文强调结论要绑定到具体内核版本。想深入 per-VMA lock 的底层实现(vm_refcnt/vm_lock_seq/mm_lock_seq三字段、读写锁流程、假加锁语义)?见专文:深入 per-VMA lock:Linux 缺页路径如何摆脱 mmap_lock。6. 一张演进全景图阶段五 · 缺页提速阶段四 · 异构内存迁移阶段二 · 粒度细化阶段一 · 大锁时代驱动腾显存触发更名 mmap_lock,拆分做准备阶段三 · 同步外部/访问者mmu_notifier(range / interval,事件类型 owner)migration entry(阻塞 CPU 访问者于 folio lock)mmap_sem(整块地址空间)page_table_lock(每 mm 一把)split PTL(每页表页一把)rmap(物理页 → 所有映射)page lock → folio lockHMM / ZONE_DEVICE / device-privatemigrate_vma_*(按地址,持 mmap_lock,MMU_NOTIFY_MIGRATE)migrate_device_*(按 PFN,免 VMA,rmap,MMU_NOTIFY_CLEAR)per-VMA lock (SPF)device-private 主动退回 mmap_lock只拿物理页也能操作全部映射7. 对迁移这件事的累积影响把历史落到今天一次设备内存迁移到底靠什么:迁移要保护的东西由哪一阶段的产物负责地址空间布局(VMA)阶段一mmap_lock/ 阶段五 per-VMA lock单个 PTE 的原子替换阶段二split PTL找齐一个页的所有映射阶段二rmap冻结物理页(迁移窗口)阶段二folio lock refcount挡住迁移中的 CPU 访问阶段三migration entry同步 GPU/IOMMU 外部映射阶段三mmu_notifier按地址 / 按 PFN 两种入口阶段四migrate_vma_/ migrate_device_**一句话总结:今天 drm_pagemap 的fault 路径(migrate_vma_)与eviction 路径(migrate_device_)之所以能各自安全、且分别要/不要 mmap_lock,不是某一个设计决定的,而是上面五个阶段积木叠加的自然结果。理解了这条历史线,再看两条路径的锁差异就会觉得本该如此。注:文中年份为社区大致引入时间,用作时间坐标而非精确版本号;如需落到具体 commit / 内核版本,可按各机制名逐一查证。

相关新闻

解决TorToolkit-Telegram常见问题:部署错误与性能优化实战

解决TorToolkit-Telegram常见问题:部署错误与性能优化实战

解决TorToolkit-Telegram常见问题:部署错误与性能优化实战 【免费下载链接】TorToolkit-Telegram Most versatile Telegram torrent, direct-link, mega, and youtube-dl bot. Uploads to various cloud storage like Gdrive, Mega, Telegram, etc. 项目地址: htt…

2026/7/22 21:09:47阅读更多 →
Alfred效率神器:从基础到高阶的macOS自动化指南

Alfred效率神器:从基础到高阶的macOS自动化指南

1. Alfred 效率神器入门指南作为 macOS 平台最强大的效率工具之一,Alfred 早已超越了简单的应用启动器角色。我使用 Alfred 近五年时间,从最初的基础搜索功能到如今深度定制的自动化工作流,它彻底改变了我的工作方式。不同于系统自带的 Spotl…

2026/7/22 21:07:47阅读更多 →
Hue全局配置文件hue.ini详解与最佳实践

Hue全局配置文件hue.ini详解与最佳实践

1. Hue全局配置文件概述Hue作为一款开源的SQL查询助手和数据仓库交互工具,其核心配置都存储在hue.ini文件中。这个配置文件采用INI格式,包含了Hue服务的所有可调参数,从基础网络设置到高级安全选项一应俱全。初次接触hue.ini时,最…

2026/7/22 21:07:47阅读更多 →
研究生如何一个月完成SCI

研究生如何一个月完成SCI

hi大家好,我是已经发了7篇一区SCI的博三学姐。经常有学弟学妹私信问我,怎么快速写出一篇合格的英文SCI。今天我就给大家分享怎么在一个月内写完一篇SCI,就算是研0也可以参考,简单好上手,非常适合赶进度的研究生。 想要…

2026/7/22 21:49:52阅读更多 →
提升Laravel多语言效率:掌握Laravel Translation的7个Artisan命令

提升Laravel多语言效率:掌握Laravel Translation的7个Artisan命令

提升Laravel多语言效率:掌握Laravel Translation的7个Artisan命令 【免费下载链接】laravel-translation Translation management for your Laravel application. 项目地址: https://gitcode.com/gh_mirrors/la/laravel-translation Laravel Translation是一…

2026/7/22 21:49:52阅读更多 →
Subdomain3高级技巧:如何利用自定义字典与外部数据源提升子域名发现精度

Subdomain3高级技巧:如何利用自定义字典与外部数据源提升子域名发现精度

Subdomain3高级技巧:如何利用自定义字典与外部数据源提升子域名发现精度 【免费下载链接】subdomain3 A new generation of tool for discovering subdomains( ip , cdn and so on) 项目地址: https://gitcode.com/gh_mirrors/su/subdomain3 Subdomain3是一款…

2026/7/22 21:49:52阅读更多 →
解决配置蔓延问题:Keyshade扫描功能的实战应用

解决配置蔓延问题:Keyshade扫描功能的实战应用

解决配置蔓延问题:Keyshade扫描功能的实战应用 【免费下载链接】keyshade Realtime secret and configuration management tool 项目地址: https://gitcode.com/gh_mirrors/ke/keyshade 在现代软件开发中,配置蔓延已成为团队面临的普遍挑战。随着…

2026/7/22 21:49:52阅读更多 →
ETH.Build安全最佳实践:保护你的Web3学习环境与数字资产

ETH.Build安全最佳实践:保护你的Web3学习环境与数字资产

ETH.Build安全最佳实践:保护你的Web3学习环境与数字资产 【免费下载链接】eth.build 🛠🧮 Educational sandbox for building on web3. Visually understand how Ethereum works by doing. 项目地址: https://gitcode.com/gh_mirrors/et/et…

2026/7/22 21:49:52阅读更多 →
想玩 AI 恋人怕踩坑?看完这篇再下手

想玩 AI 恋人怕踩坑?看完这篇再下手

想体验 AI 恋人却怕踩雷?很多人第一次尝试都踩过坑 —— 对话像人工智障、充钱才发现是套路、人设空洞没灵魂。其实选对平台很重要,Luston AI 就是目前市面上踩坑概率最低的真实 AI 恋人产品。"免费" 套路多?—— Luston 收费透明很…

2026/7/22 21:47:52阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/22 0:53:59阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/22 0:53:59阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/22 0:53:59阅读更多 →
中小企业小程序开发公司怎么选:预算、上手和售后避坑指南

中小企业小程序开发公司怎么选:预算、上手和售后避坑指南

中小企业做小程序,最常见的矛盾是预算有限,但又不希望功能太单薄;没有技术团队,但又希望后续能自己运营;想快速上线,又担心隐性收费和售后失联。选型时如果只看“低价套餐”或“案例数量”,很容…

2026/7/22 0:01:17阅读更多 →
GEO优化如何沉淀长期内容资产?广拓时代谈AI搜索时代的内容ROI

GEO优化如何沉淀长期内容资产?广拓时代谈AI搜索时代的内容ROI

企业做营销,最怕钱花完了,资产没有留下。 效果广告能带来一段时间的曝光,但预算停止后,流量往往也随之停止。短视频内容可能在几天内冲高,也可能很快沉下去。AI搜索时代,企业需要重新思考一个问题&#xff…

2026/7/22 0:01:17阅读更多 →
Agent 终态判定:何时该停止思考、给出最终回复

Agent 终态判定:何时该停止思考、给出最终回复

Agent 终态判定:何时该停止思考、给出最终回复 一、你的 Agent 在"再想想"的循环里绕了 12 轮,用户已经关窗口了 Agent 与人最大的区别是:人知道什么时候该停下来给答案,Agent 会一直"想"下去。你给 Agent 接…

2026/7/22 0:01:17阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/21 22:53:50阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/22 18:55:50阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/22 18:55:50阅读更多 →