内存映射mmap是Linux内核的一个重要机制,它和虚拟内存管理以及文件IO都有直接的关系,这篇细说一下mmap的一些要点。
修改(2015-11-12):Linux的虚拟内存管理是基于mmap来实现的。vm_area_struct是在mmap的时候创建的,vm_area_strcut代表了一段连续的虚拟地址,这些虚拟地址相应地映射到一个后备文件或者一个匿名文件的虚拟页。一个vm_area_struct映射到一组连续的页表项。页表项又指向物理内存page,这样就把一个文件和物理内存页相映射。
来理解一下虚拟地址映射的过程:拿到一个虚拟地址,根据已有的vm_area_struct看这个虚拟地址是否属于某个vm_area_struct
- 如果没有匹配到,就报段错误,访问了一个没有分配的虚拟地址。
- 如果匹配到了vm_area_struct,根据虚拟地址和页表的映射关系,找到对应的页表项PTE,如果PTE没有分配,就报一个缺页异常,去加载相应的文件数据到物理内存,如果PTE分配,就去相应的物理页的偏移位置读取数据
所以虚拟页的三种状态的实际含义如下:
- 未分配虚拟页,指的是没有使用mmap建立vm_area_struct,所以也就没有对应到具体的页表项
- 已分配虚拟页,未映射到物理页,指的是已经使用了mmap建立的vm_area_struct,可以映射到对应的页表项,但是页表项没有指向具体的物理页
- 已分配虚拟页,已映射到物理页,指的是已经使用了mmap建立的vm_area_struct,可以映射到对应的页表项,并且页表项指向具体的物理页
mmap要么映射到一个后备文件,要么映射到一个匿名文件。操作系统分配物理内存时实际用到了匿名文件的mmap。
mmap和虚拟内存管理
先来看看Linux内核的用户进程虚拟内存管理。内核定义了mm_struct结构来表示一个用户进程的虚拟内存地址空间。
1. start_code, end_code指定了进程的代码段的边界,start_data, end_data指定了进程数据段的边界。在ELF二进制文件映射到虚拟内存地址空间后,这几个长度就不会再改变
2. start_brk, brk指定了堆的边界。start_brk表示堆的起始地址,在进程整个生命周期不会改变,brk表示堆的结束位置,会随着堆的长度改变而改变
3. stack_top指定了栈的起始位置,一般是
4. task_size指定了用户进程地址空间的长度,也就是用户进程地址空间的顶部边界