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/10/12 0:00:56 阅读更多 →
迁移学习新视角:无语义噪声也能助力模型,提升少量标注场景学习效果!

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

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

2026/10/6 5:10:08 阅读更多 →
FX5U与R系列通信怎么选?三菱PLC协议差异全解析

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

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

2026/10/12 21:29:55 阅读更多 →

最新新闻

职场送礼预算有限?五款高颜值水杯选品与实用攻略

职场送礼预算有限?五款高颜值水杯选品与实用攻略

“预算有限,但要送得有面子”这件事,基本上每个职场人都绕不过去。逢年过节、项目收官、出差归来,总得给同事带点手信,给领导表表心意。送太贵的心疼钱包,送太便宜的又怕拿不出手,水杯这种品类反而成了最优…

2026/10/13 4:53:16 阅读更多 →
VirtualBox CPU Manager中MSR模块深度解析

VirtualBox CPU Manager中MSR模块深度解析

1. 为什么深入CPU Manager中的MSR模块是VirtualBox源码分析的分水岭在VirtualBox源码分析的整个学习路径中,前11讲大多围绕设备模拟、内存管理、虚拟机生命周期控制等“看得见、摸得着”的模块展开——比如VMM(Virtual Machine Monitor)初始化…

2026/10/13 4:53:16 阅读更多 →
电力物资招标中标候选人公示怎么看?投标策略与评标机制深度解读

电力物资招标中标候选人公示怎么看?投标策略与评标机制深度解读

一份省级电网公司2026年第一批物资招标中标候选人公示,放在电力招标圈里算不上大新闻,但每次出来都值得认真看一遍。电力物资招标是观察电网一年投资方向最直接的窗口,候选人名单背后藏着设备选型偏好、供应商格局变化和采购策略调整。这篇文…

2026/10/13 4:53:16 阅读更多 →
信号与系统多普勒大作业:从傅里叶变换到频谱分析的完整实现

信号与系统多普勒大作业:从傅里叶变换到频谱分析的完整实现

简介:面向西电通信工程学院学生的信号与系统大作业资源,聚焦多普勒效应在通信系统中的原理推导、信号建模与仿真分析,适合正在完成同类课程设计,或希望结合实际问题巩固信号处理知识的本科学习者。压缩包共六个文件,包…

2026/10/13 4:53:16 阅读更多 →
VirtualBox CPUID虚拟化原理与动态映射机制解析

VirtualBox CPUID虚拟化原理与动态映射机制解析

1. 为什么CPUID是虚拟化CPU管理的“第一道门”在VirtualBox源码里翻到CPUM模块时,很多人会下意识跳过CPUMR3CpuId这类函数,觉得“不就是读个CPU型号和特性吗?调用一下cpuid指令完事”。我第一次也是这么想的——直到在某次跨平台兼容测试中&a…

2026/10/13 4:53:16 阅读更多 →
大角几何Beta版:函数可视化教学回归动态变化本源

大角几何Beta版:函数可视化教学回归动态变化本源

第一次打开大角几何Beta版的时候,我其实是带着一点怀疑的。教了十几年函数,我见过的“可视化教学工具”少说也有七八种,玩法无非是把黑板上的静态图像搬到屏幕上,颜色丰富一点、拖动流畅一点,本质上还是展示一个结果。…

2026/10/13 4:52:16 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →