Lazy loaded image
Words 0Read Time 1 min
Invalid Date
🔗
核心结论
mmap 在驱动里的执行链路,本质不是“内核帮你返回一个地址”,而是:用户态发起映射请求 → 内核建立 VMA 描述 → 驱动 .mmap 做策略决策 → 页表把底层页或页框映射进用户虚拟地址空间。

专题导航

概述

对于嵌入式内核与驱动开发者来说,理解 mmap 的关键不在于系统调用原型,而在于请求是如何一路落到驱动,并最终变成页表映射。这一链路决定了你该在哪一层做范围校验、权限控制、页属性设置和生命周期绑定。
 
如果只知道 file_operations 里有个 .mmap,却说不清 VMA、页属性和具体映射方式,代码大概率也会写成“能跑但不稳”的试验品。

架构框架

分点细节

1. 用户态 mmap() 只是提出映射需求

典型调用如下:
这里用户态声明了几件事:
  • 想映射多大
  • 以什么权限访问
  • 是共享还是私有
  • 从设备对象的哪个偏移开始映射
 
但真正是否允许、映射到什么对象、用什么页属性,决定权不在用户态,而在内核和驱动。

2. 内核先建立 VMA,再把策略控制权交给驱动

vm_area_struct 表示进程地址空间中的一段虚拟内存区域,里面描述了:
  • 起止虚拟地址
  • 访问权限
  • 页保护属性
  • 关联文件对象
  • 可选的 vm_ops
 
你可以把 VMA 理解成“这段映射关系的合同文本”。驱动后面做的所有处理,基本都围绕这份合同展开。

3. 驱动 .mmap 是策略落点

在字符设备场景下,最后会进入驱动的 .mmap 回调。这里通常要完成:
  • 校验 offsetsize 是否落在允许范围内
  • 判断映射对象究竟是寄存器、DMA buffer 还是共享页
  • 调整 vma->vm_page_prot
  • 按对象类型选择建立映射的方法
 
这一层的职责不是“把指针交给用户”,而是决定页表应该如何合法地建立

4. 常见映射建立方式

remap_pfn_range()
适合把一段物理页框直接映射到用户空间,常见于:
  • MMIO 寄存器窗口
  • 连续物理内存
  • 部分 framebuffer / DMA 场景
vm_insert_page() / vmf_insert_*
适合驱动手里已经持有 struct page 的情况,更适合页级粒度插入。
vm_operations_struct
当你需要更复杂的缺页处理或生命周期管理时,可以结合:
  • open
  • close
  • fault
 
这类设计常见于按页懒建立映射,或者需要精细追踪资源持有关系的驱动。

5. 真正被建立的是用户虚拟地址到目标页的映射关系

这一点非常关键:
  • 用户拿到的是用户虚拟地址
  • 不是内核把某个裸指针直接塞给用户
  • 也不是做了一份数据拷贝
 
一旦页表建立完成,后续大多数数据访问都不再经过 read/write 的复制路径,这也是 mmap 高性能与高风险并存的原因。

实践指南

先画清楚你的映射链路

动手前先回答以下问题:
  1. 用户传进来的 offset 对应哪一类对象?
  1. 是整段一次性映射,还是按页处理?
  1. 用户态访问的是数据面还是控制面?
  1. 这段映射之后,谁负责释放和失效处理?

.mmap 中保持职责单一

更稳的实践是把 .mmap 划成几步:
  • 参数校验
  • 对象分发
  • 页属性设置
  • 建立映射
  • 绑定生命周期
 
别把业务逻辑、权限判断、调试开关和映射细节揉成一锅,这种代码通常第二年就没人敢碰。

为不同对象准备独立映射函数

例如:
  • map_regs_to_user()
  • map_dma_buffer_to_user()
  • map_shared_pages_to_user()
 
这样不仅清晰,也方便后续对不同对象附加不同的属性和限制。

易错点分析

  • .mmap 理解成返回内核指针:这是概念错误,真正建立的是页表映射。
  • 只校验长度,不校验偏移:用户一旦能跳到未授权窗口,安全边界就废了。
  • 默认页属性不管:很多诡异问题就埋在这里,尤其是寄存器和 DMA。
  • 不区分一次性映射与缺页驱动映射:复杂对象的生命周期会因此失控。
  • 映射成功就以为结束了:实际上后面的同步、失效、回收才是长期战场。

词汇表

  • VMAvm_area_struct,进程地址空间中的一段虚拟内存描述。
  • PFN:Page Frame Number,物理页框号。
  • remap_pfn_range():将物理页框范围映射到用户虚拟地址空间的常用接口。
  • vm_insert_page():将具体页对象插入用户 VMA 的接口。
  • vm_operations_struct:与 VMA 生命周期和缺页处理相关的操作集合。

关联和跳转

  • 如果映射对象是 DMA buffer,继续阅读:
 
上一篇
Data Structure and Algorithm
下一篇
用面试拷问嵌入式技术栈

Comments
Loading...