mmap
1 概念
mmap:内核空间映射到用户空间虚拟地址上。之后,应用程序直接访问映射后的虚拟地址,实际是在访问内核空间
2 流程
mmap :为申请的地址空间建立合适的页表
3 申请物理内存
3.1 kmalloc
内核中,申请连续的物理内存,用于小内存分配。
作用:内核中用于指定字节大小的内存分配函数,跟应用层的malloc()和free()对应。
// kmalloc返回的虚拟地址和物理地址有一个固定偏移关系 void *kmalloc(size_t size,gfp_t flags); // 需要指定gfp_t void kfree(const void *ptr)
3.2 vmalloc
内核为了满足某些大块内存申请需求,提供了一组函数:
void *vmalloc(unsigned long size) 参数:要申请的内存块的大小 void vfree(void *addr)
注意:用于分配一块非连续的地址空间,物理地址一般是非连续的,但是虚拟地址是连续的,分配的内存空间 被映射进入内核数据段中,从用户空间是不可见的。
这些不连续的物理块,内核中用结构struct vm_struct来表示。
描述:这是一个链表,记录vmalloc所得到的每块的地址、大小和对应的页面等信息。
3.3 vmalloc_user()
作用:功能类似于vmalloc(),在vmalloc()的基础上将地址空间清零,这样该地址空间被映射到用户空间不会发生数据泄露。 一般情况下,这段虚拟内存是当前进程空间的,因此会给它添加一个VM_USERMAP的flag 。防止将kernel space 的数据泄露到user space。
#include<linux/vmalloc.h> void *vmalloc_user(unsigned long size) { void *ret; #首先通过__vmalloc申请一段大小为size的虚拟内存 ret = __vmalloc(size, GFP_KERNEL | __GFP_ZERO, PAGE_KERNEL); if (ret) { struct vm_area_struct *vma; down_write(¤t->mm->mmap_sem); #在当前进程空间中查找这段虚拟空间,一般情况下可以找到,找到后或上VM_USERMAP vma = find_vma(current->mm, (unsigned long)ret); if (vma) vma->vm_flags |= VM_USERMAP; up_write(¤t->mm->mmap_sem); } #返回虚拟地址,r如果失败的话,则返回null return ret; }
3.4 对比
3.4.1 kmalloc和vmalloc
相同:
-
都是申请内核空间的物理内存
不同:
-
物理内存是否连续:kmalloc用于申请较小的,连续的物理内存
vmalloc申请较大的,不连续的物理内存
-
虚拟内存位置(见下图): kmalloc()分配的内存处于3GB~ high_memory(低端内存)
vm_struct 分配的内存在VMALLOC_START ~ 4GB(高端内存)之间。相邻的vm_struct之间都有4K的安全间隙。
-
物理地址对应关系:kmalloc返回的虚拟地址和物理地址由一个固定偏移关系,vmalloc返回的虚拟地址 和物理地址 没有线性关系,需要遍历页表才可以找到物理地址
-
kmalloc一般用于给数据结构分配内存,DMA(容易获得物理地址);vmalloc用在为活动的交换区 分配数据,为I/O驱动程序分配缓冲区,或为模块分配空间
3.4.2 vmalloc和vmalloc_user
区别:
4 remap
4.1 remap_pfn_range()
应用:将kmalloc()申请的内存映射到用户空间
函数作用:为一段物理地址(addr ~ addr+size 之间)建立新的页表,pfn指向实际的RAM时使用
函数原型: int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn, unsigned long size, pgprot_t prot); 参数: vma :虚拟内存区域:在一定范围内的页将被映射到该区域内 addr:重新映射时,用户空间虚拟地址起始处 size: 被重新映射的区域大小,字节为单位 pfn:与物理内存对应的页帧号,虚拟内存将要被映射到该物理内存上,页帧号只是将物理地址右移PAGE_SHIFT位(物理地址 >> PAGE_SHIFT) prot:保护属性 vma->vm_page_prot 返回值: 成功:0 错误:负的错误码
4.2 remap_vmalloc_range()
类似remap_pfn_range()
/** * remap_vmalloc_range - map vmalloc pages to userspace * @vma: mmap使用调用传下来的vma to cover (map full range of vma) * @addr: vmalloc分配内存的起始地址 * @pgoff: 偏移参数,根据vma->vm_pgoff获取 */ int remap_vmalloc_range(struct vm_area_struct *vma, void *addr, unsigned long pgoff) { return remap_vmalloc_range_partial(vma, vma->vm_start, addr, pgoff, vma->vm_end - vma->vm_start); } EXPORT_SYMBOL(remap_vmalloc_range);