浅谈ioremap,vmalloc,mmap三者之间的实现&区别
前言
系统mmu开启后, 程序对内存的访问都是虚拟地址, 之后mmu会自动将虚拟地址变为实际的物理地址(硬件行为), 所以我们的程序如果要访问物理地址的话,必须要通过mmu建立虚拟地址与物理地址之间的映射关系。对于虚拟地址映射到物理地址的操作, 涉及到3个典型的函数.
- ioremap
- 在driver中使用,一般用于映射registers address。
- vmalloc
- 在dirver中使用,在内存的vmalloc区申请一块连续的虚拟空间,生成虚拟空间所对应的物理地址并非连续。
- mmap
- 在应用层使用,实现在应用层也可以访问物理地址
上述阐述了三个函数的使用场景,但同样都是虚拟地址映射到物理地址,它们之间有什么区别呢? 这个问题必须要了解它们的实现才能明白,我们并不打算详细的分析代码,而是给出大体关键的脉络来解决,想通这个问题。
ioremap 与 vmalloc
这两个函数之所以放在一起讲,是因为它们的实现几乎是一样的。
这两个函数都是从VMALLOC区(内核虚拟地址空间)分配一个空闲的虚拟地址空间(※注意是虚拟地址空间), 我们看张图来理解一下函数的实现
首先,VMALLOC区 是由两个结构体维护的,即:struct vmap_area和 struct vm_struct。
其中vmap_area是专门用来管理 VMALLOC区域的虚拟空间。
即,一个vmap_area结构代表一块有效的虚拟空间, 当调用vmalloc函数或者ioremap时:
- 首先通过kmalloc创建一个 vmap_area结构体。
- 遍历vmap_area_root红黑树,找到VMALLOC区中的一个虚拟地址addr,这个addr与树中所有vmap_area结构体所管理的地址范围都没有重合。
- 之后将addr赋值到新创建的vmap_area结构体中,并插入到 vmap_area_root的红黑树中。
以上,新创建的 vmap_area结构体管理着addr地址以及size范围,即虚拟地址已经申请好了,那又如何映射到物理地址呢?
这里就需要 struct vm_struct结构体来管理了,vm_struct记录着虚拟地址与物理地址之间的关系。
我们紧接着上面的流程:
- vmap_area结构被赋值好后,会把其中的虚拟地址信息赋值到新创建的vm_struct中,之后
- 对于ioremap函数,知道了虚拟地址addr,知道了物理地址:寄存器地址(传入的参数),直接调用映射函数完成对mmu的编程,实现addr与寄存器地址之间的映射关系
- 对于vmalloc函数,知道了虚拟地址addr后,从伙伴系统中申请size(vmalloc函数的参数)大小的物理地址空间,之后调用映射函数完成虚拟地址与物理页之间的映射关系。
总结:
- vmalloc与ioremap在驱动中才能用,因为申请的虚拟内存是在内核的地址范围内,是从VMALLOC区域申请的。
- ioremap从VMALLOC区申请到虚拟地址后,直接映射(因为物理地址已知): virtual addr <–> regsiters address。
- vmalloc从VMALLOC区申请到虚拟地址后,需要申请size大小的物理地址空间(vmalloc参数指定),然后在映射: virtual addr <–> 物理地址。
mmap
mmap,即实现:在应用层的虚拟地址空间(0->3G)中,找到一块可用的虚拟地址(vma),然后将这块虚拟空间的地址范围传入到driver,在driver中实现映射:vma<–>物理地址(一般显示driver经常这么用)
上图中我们可以看到,应用层的虚拟空间也类似于内核空间的VMALLOC区,也需要一个结构体管理:vm_area_struct,我们简称vma.不同的是,应用虚拟空间的管理,是在进程描述符中维护的,即不同的进程管理不同的vma。
对于一个进程的0-3G的虚拟空间,用vm_area_struct结构体来维护,一个vma代表一块有效的虚拟地址空间。
同一个vma存在于两个地方,一个是在mmap这个变量中,用链表维护着,方便统计,一个是在mm_rb所在的红黑树中维护,方便遍历查找。
我们通过上图中红框框的部分来描述mmap的实现过程(类似于上面的两个函数)
- 首先,遍历mm_rb管理的红黑树,找出0-3G空间中的可用地址范围
即,找到的地址addr,不能跟树中的所有vm_area_struct所管理的地址范围重合 - 创建一个vma(vm_area_struct结构体), 将addr以及范围size赋值到此结构体中.
- 把vma传给driver, error = file->f_op->mmap(file, vma);
- driver中调用f_op->mmap,因为要映射的虚拟地址vma通过参数传进来了, 所以仅调用remap_pfn_range相关函数即可完成映射(物理地址空间是driver提前准备好的)
- 如果映射成功,将vma 插入到mmap链表中,然后在插入到红黑树mm_rb中
- __vma_link
总结
- ioremap与vmalloc 是在内核虚拟空间的VMALLOC区申请虚拟地址
- mmap是在当前进程的user虚拟空间申请虚拟内存
更多推荐
所有评论(0)