Linux 内核中 mm_struct 引用计数与从驱动安全访问进程地址空间
内核中 mm_struct 引用计数与从驱动安全访问进程地址空间本文系统梳理内核mm_struct的两套引用计数(mm_users/mm_count)及其配套 API(mmget/mmput、mmgrab/mmdrop、mmget_not_zero、get_task_mm等),并给出内核驱动需要操作某个进程的 VMA / 页表数据时,如何判定该进程是否还活着的标准范式。全文以drivers/gpu/drm/drm_pagemap.c的 SVM 迁移代码为实例。1. 为什么需要两个计数struct mm_struct描述一个进程的整个用户态地址空间(VMA 树、页表、mmap_lock等)。它的生命周期有两个层次不同的问题:地址空间(用户态映射)是否还在?—— VMA、页表项、匿名内存是否还有效。mm_struct这块结构体本身是否还没被free?—— 即便地址空间已经拆掉,结构体可能仍需短暂存活(例如内核线程借用它做 lazy TLB)。内核用两个 atomic 计数分别回答这两个问题:计数语义增/减 API保护对象mm_users有多少使用者需要访问用户态地址空间mmget/mmget_not_zero/mmputVMA、页表、用户态映射mm_count有多少引用需要mm_struct结构体本身存活mmgrab/mmdropstruct mm_struct内存关键关系(定义于include/linux/sched/mm.h、kernel/fork.c):mm_users归 0→ 触发__mmput():调用exit_mmap()拆除所有 VMA、释放页表和用户态页、触发 mmu_notifier release 等。地址空间从此不可访问。mm_users本身对mm_count持有1 个引用。因此只要mm_users 0,mm_count必然 0。mm_count归 0→ 触发__mmdrop():真正free掉mm_struct结构体(以及 pgd 等)。一句话:mm_users保护地址空间可用,mm_count只保护结构体没被释放。结构体还在(mm_count0)不代表地址空间还能访问(可能mm_users0)。2. 计数生命周期图mm_count (struct mm_struct)mm_users (address space)释放内部那 1 个引用后 mmdropmm_users 对 mm_count 持有 1 个引用mmget() / mmget_not_zero()1 mm_usersmmput()-1 mm_usersmm_users 0__mmput()exit_mmap() 拆 VMA/页表 mmu_notifier releasemmgrab()1 mm_countmmdrop()-1 mm_countmm_count 0__mmdrop()free(mm_struct) free pgd进程退出(do_exit→exit_mm→mmput)时,通常先把mm_users降到 0,地址空间被exit_mmap()拆掉;但如果此时还有人持有mm_count(如 lazy TLB 的内核线程、或驱动mmgrab了),mm_struct结构体会继续存活到最后一个mmdrop。3. API 逐个说明以下摘自include/linux/sched/mm.h。3.1 mmgrab / mmdrop —— 操作mm_countstaticinlinevoidmmgrab(structmm_struct*mm){atomic_inc(mm-mm_count);}staticinlinevoidmmdrop(structmm_struct*mm){if(unlikely(atomic_dec_and_test(mm-mm_count)))__mmdrop(mm);}作用:只保证mm_struct结构体不被释放,不保证地址空间可用。适用:需要长期(甚至无界时间)持有对mm的弱引用,后续再用mmget_not_zero尝试升级为可访问地址空间的强引用。头文件注释原话:“This doesn’t guarantee that the associated address space will still exist later on and mmget_not_zero() has to be used before accessing it.”3.2 mmget / mmput —— 操作mm_usersstaticinlinevoidmmget(structmm_struct*mm){atomic_inc(mm-mm_users);}externvoidmmput(structmm_struct*);voidmmput_async(structmm_struct*);/* 可在原子上下文调用的慢路径版本 */作用:保证用户态地址空间在mmput前不被拆除,可以安全遍历 VMA、访问页表(仍需按需持mmap_lock/ PTL,见第 5 节)。mmget只在你已确定mm_users 0(例如就是current-mm)时使用,因为它无条件1,对一个已经归 0 的mm用mmget会造成复活已死地址空间的错误。头文件注释警告:“Never use this function to pin this address space for an unbounded/indefinite amount of time.”—— 因为它会阻止进程退出时回收内存。mmput_async:当你可能在原子上下文(持自旋锁、IRQ 等)释放最后一个引用时使用,把可能睡眠的__mmput(内含exit_mmap)甩到 workqueue。3.3 mmget_not_zero —— 安全地升级为地址空间引用staticinlineboolmmget_not_zero(structmm_struct*mm){returnatomic_inc_not_zero(mm-mm_users);}语义:仅当mm_users ! 0时才1,并返回是否成功。返回true:抢到了一个 user 引用,地址空间保证存活到你配对的mmput()/mmput_async(),可以安全访问 VMA / 页表。返回false:mm_users已经是 0 —— owner 进程正在退出、exit_mmap()已经或即将拆地址空间。绝不能再访问它的 VMA / 页表。这是驱动持有别的进程mm时访问其地址空间的唯一正确入口。3.4 get_task_mm / mm_access —— 从 task 取 mmexternstructmm_struct*get_task_mm(structtask_struct*task);externstructmm_struct*mm_access(structtask_struct*task,unsignedintmode);get_task_mm:在task_lock下读task-mm,若非内核线程则mmget后返回(即返回时已持有mm_users引用)。用完mmput。mm_access:在get_task_mm基础上叠加ptrace_may_access权限检查,用于跨进程访问(如/proc/pid/mem)。4. 选择矩阵:该用哪个你的需求用什么释放就地访问current自己的地址空间已隐含mm_users0,直接mmap_read_lock(current-mm)解锁即可从task_struct拿到某进程 mm 并访问get_task_mm()/mm_access()mmput()长期缓存别的进程的 mm 指针(弱引用)mmgrab()保命mmdrop()之后要真正访问被缓存的那个 mm 的地址空间先mmget_not_zero()判活成功后mmput()/mmput_async()可能在原子上下文释放最后引用mmput_async()—驱动里典型的两段式:注册时mmgrab缓存mm(弱引用,允许进程退出);真正要迁移/访问地址空间那一刻,mmget_not_zero判活并升级,失败就放弃。5. 计数 ≠ 锁:仍需 mmap_lock / PTL引用计数只解决这块 mm / 地址空间还在不在,不解决并发修改。访问地址空间时还需要相应的锁:按虚拟地址遍历 VMA(vma_lookup、读vm_start/vm_end):需要mmap_read_lock(mm),以排除并发munmap/mremap/mprotect(它们持mmap_write_lock)。修改单个 PTE(present ↔ migration entry 的原子替换):需要 PTL(pte_offset_map_lock/pmd_lock)。以page 为中心、走 rmap定位 PTE(如migrate_device_*):不需要mmap_lock,但需要 folio lock 反向映射时的anon_vma锁 PTL。组合规则:mmget_not_zero()成功 “地址空间还在,可以去拿锁”;具体访问 VMA 还要mmap_read_lock,改 PTE 还要 PTL。两者缺一不可。6. 实例:drm_pagemap SVM 迁移6.1 缓存弱引用(注册期)SVM BO 初始化时保存所属进程的mm(drivers/gpu/drm/drm_pagemap.cdrm_pagemap_devmem_init()):devmem_allocation-mmmm;/* 通常调用方已 mmgrab 保命 */BO 可能长期存活(显存里放着),而 owner 进程随时可能退出,所以这里只能是弱引用语义 —— 不能用mm_users钉住,否则进程退不干净。6.2 fault 路径:mmap_lock 已由缺页框架持有drm_pagemap_populate_mm()是填充 device 内存的入口,它先判活再上锁:intdrm_pagemap_populate_mm(structdrm_pagemap*dpagemap,...,structmm_struct*mm,...){if(!mmget_not_zero(mm))/* ① 判活并升级为地址空间引用 */return-EFAULT;mmap_read_lock(mm);/* ② 再锁 VMA */errdpagemap-ops-populate_mm(dpagemap,start,end,mm,...);mmap_read_unlock(mm);mmput(mm);/* ③ 归还引用 */returnerr;}这就是第 4、5 节范式的教科书式落地:mmget_not_zero判活 →mmap_read_lock锁 VMA → 访问 → 解锁 →mmput。6.3 eviction 路径:判活后走 page-centric,不锁 VMAdrm_pagemap_evict_to_ram()由驱动主动腾显存时调用,访问的可能是另一个进程的 BO:retry:if(!mmget_not_zero(devmem_allocation-mm))/* 判活 */return-EFAULT;/* owner 正在退出 */...errmigrate_device_pfns(src,npages);/* page-centric靠 rmap不遍历 VMA */...err_out:mmput_async(devmem_allocation-mm);/* 可能在受限上下文用 async */if(completion_done(devmem_allocation-detached))return0;/* BO 已被并发释放视作成功 */if(retry_count--){cond_resched();gotoretry;}returnerr?:-EBUSY;要点:mmget_not_zero成功只是保活地址空间;这里并不mmap_read_lock,因为migrate_device_pfns以 page 为中心、通过 rmap 定位并在 PTL 下替换 PTE(见第 5 节),无需按地址遍历 VMA。用mmput_async而非mmput,因为释放最后一个引用会触发exit_mmap这类可能睡眠的重活,而本路径可能在不宜睡眠的上下文中收尾。mmget_not_zero返回false → -EFAULT是owner 进程正在退出的唯一信号来源。6.4 -EFAULT 的下游语义(驱动侧处理)在多进程显存过量(overcommit)场景中,进程 A 分配 VRAM 会触发 TTM 驱逐进程 B 的 SVM BO;若此刻 B 被CtrlC杀掉,drm_pagemap_evict_to_ram的mmget_not_zero(B-mm)失败返回 -EFAULT。该错误若原样上抛,会被 TTM 当作致命错误中止整个 LRU 驱逐 walk,从而误伤 A 的分配。amdgpu 侧的正确处理是把这个owner 正在退出的 -EFAULT 翻译成可重试的 -EBUSY(amdgpu_svm_bo_evict()),让 TTM 跳过这个 victim 去驱逐别的 BO,而 B 的exit_mmap会异步回收其 device 页并释放 BO:rdrm_pagemap_evict_to_ram(sbo-devmem);if(r-EFAULT)/* owner 退出中页将被 exit_mmap 回收 */r-EBUSY;/* 让 TTM 换一个 victim而非致命失败 */这印证了本文的核心结论:mmget_not_zero失败不是错误,而是进程已死这一合法状态的信号,上层应把它当作该对象即将被自然回收来处理,而不是硬失败。7. 常见错误与陷阱只mmgrab就去访问 VMA:结构体在不代表地址空间在。必须再mmget_not_zero判活。对可能已死的mm用mmget(而非mmget_not_zero):会把mm_users从 0 拉回非 0,复活一个正在/已经exit_mmap的地址空间,导致 UAF / 数据损坏。持有mm_users无界时间:阻止进程退出时回收内存,等价于内存泄漏 拖住 OOM。弱引用要用mmgrab。把mmget_not_zero失败当致命错误上抛:在跨进程场景会误伤无辜的其他进程(见 6.4)。在原子上下文用mmput:最后一次mmput会走exit_mmap可能睡眠;受限上下文用mmput_async。有了引用就不加锁:计数不等于锁,访问 VMA 仍需mmap_lock,改 PTE 仍需 PTL。8. 速查表我持有我能保证我能做我不能做mm_count(mmgrab)mm_struct结构体不被 free读mm指针字段、后续mmget_not_zero判活访问 VMA / 页表 / 用户内存mm_users(mmget/mmget_not_zero成功)地址空间不被拆加mmap_lock遍历 VMA、加 PTL 改 PTE无界长期持有mmap_read_lockVMA 结构不被并发改按地址遍历 VMA修改 VMA(需 write)PTL单个 PTE 原子性present↔migration entry 替换遍历 VMA

相关新闻

C++实现LSB算法:图片信息隐藏与提取实战指南

C++实现LSB算法:图片信息隐藏与提取实战指南

1. 项目概述:当图片成为“隐形信封”几年前,我接手过一个需求,客户需要在社交媒体上分享一批设计图,但又不想让原始设计参数(比如某个零件的精确尺寸)被轻易截取。直接发图,水印容易被裁剪&…

2026/7/23 7:36:57 阅读更多 →
迁移学习新视角:无语义噪声也能助力模型,提升少量标注场景学习效果!

迁移学习新视角:无语义噪声也能助力模型,提升少量标注场景学习效果!

迁移学习新突破:噪声助力模型学习 迁移学习里的 "源数据",未必非得是图像、文本或音频。一组从高斯分布里随机采样出来、没有任何语义的噪声,也能帮助模型在少量标注下学得更好。这项工作名为半监督噪声自适应(Semi - S…

2026/7/23 7:36:57 阅读更多 →
FX5U与R系列通信怎么选?三菱PLC协议差异全解析

FX5U与R系列通信怎么选?三菱PLC协议差异全解析

三菱 PLC 覆盖串口、以太网、现场总线、工业以太网四大类,特点是自有协议强(CCLink/MC/SLMP)、Modbus 全系标配、高端加 EtherCAT/OPC UA;和汇川相比,CCLink 生态更成熟,EtherCAT 起步稍晚。一、串口协议&a…

2026/7/23 7:36:57 阅读更多 →

最新新闻

MatriMax游戏魔方:磁力双模切换与智能交互解析

MatriMax游戏魔方:磁力双模切换与智能交互解析

1. 开箱初体验:当魔方遇上游戏手柄 从快递盒里取出MatriMax游戏魔方的瞬间,我就被这个三阶魔方的精致做工惊艳到了。不同于传统魔方光滑的塑料表面,它的每个色块都采用了细腻的磨砂处理,转动时指尖传来的阻尼感恰到好处——既不会…

2026/7/23 13:58:44 阅读更多 →
10款企业文件共享平台对比:中小企业如何选择合适网盘

10款企业文件共享平台对比:中小企业如何选择合适网盘

为什么中小企业文件共享不只是"选个网盘" 中小企业从个人网盘、微信群、邮件附件过渡到企业文件管理工具时,最容易踩的坑是:以为"能存文件、能分享链接"就够了。 但用上几个月后,问题就来了:文件散落在不同…

2026/7/23 13:58:44 阅读更多 →
AI知识抽取技术:从原理到工程实践

AI知识抽取技术:从原理到工程实践

1. 知识抽取技术概述:AI原生应用的核心引擎知识抽取技术作为构建AI原生应用的基础设施,正在重塑企业知识管理的范式。这项技术通过自动化手段从非结构化数据中提取结构化知识单元,包括实体、属性和关系,形成机器可理解、可推理的知…

2026/7/23 13:58:44 阅读更多 →
第30篇 QT-vs2019编译后exe版本自动更新

第30篇 QT-vs2019编译后exe版本自动更新

QT vs项目中:Resource Files右键 添加RC文件 文件名为:VersionInfo.rc 项目目录找到:

2026/7/23 13:58:44 阅读更多 →
解决黑马[天机学堂]出现账号无法登录显示服务不存在问题

解决黑马[天机学堂]出现账号无法登录显示服务不存在问题

在黑马天机学堂微服务实操过程中,经常出现用户账号无法正常登录的问题。问题原因用户登录功能完全依赖 tj-auth 认证服务,如果该服务没有启动、或者没有注册到 Nacos,前端就无法完成登录校验,导致登录失败。解决完整流程jenkins先…

2026/7/23 13:58:44 阅读更多 →
科技巨头AI军备竞赛:资本逻辑与算力基建

科技巨头AI军备竞赛:资本逻辑与算力基建

1. 科技巨头AI军备竞赛的资本逻辑2025-2026年全球科技产业最引人注目的现象,莫过于七大科技巨头(微软、苹果、亚马逊、Alphabet、Meta、英伟达和特斯拉)在AI领域累计超过6500亿美元的资本开支计划。这个数字相当于越南2025年全年GDP的1.5倍&a…

2026/7/23 13:57:44 阅读更多 →

日新闻

从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表)

从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表)

更多请点击: https://intelliparadigm.com 第一章:从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表) 当AI副业主理人不再仅满足于单次服务交付,而是主动构建可复用、可裂变、可…

2026/7/23 0:00:25 阅读更多 →
AI写作开头钩子设计:为什么你的AI文案完读率不足18%?——基于2,346篇A/B测试报告的归因分析

AI写作开头钩子设计:为什么你的AI文案完读率不足18%?——基于2,346篇A/B测试报告的归因分析

更多请点击: https://codechina.net 第一章:AI写作开头钩子设计:为什么你的AI文案完读率不足18%?——基于2,346篇A/B测试报告的归因分析 在对2,346篇跨行业AI生成文案的A/B测试数据进行聚类分析后,我们发现&#xff1…

2026/7/23 0:01:26 阅读更多 →
Chitchatter完整指南:免费开源的终极点对点安全聊天工具

Chitchatter完整指南:免费开源的终极点对点安全聊天工具

Chitchatter完整指南:免费开源的终极点对点安全聊天工具 【免费下载链接】chitchatter Secure peer-to-peer chat that is serverless, decentralized, and ephemeral 项目地址: https://gitcode.com/gh_mirrors/ch/chitchatter Chitchatter是一款革命性的安…

2026/7/23 0:01:26 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/7/22 12:54:44 阅读更多 →

月新闻