内核版本: 4.1
函数原型
|
这是mmap的函数原型,而系统调用的接口在mm/map.c中的:
|
虚拟内存管理
这里我们先介绍两个关于虚拟内存的数据结构。虚拟内存概念的相关资料网上已经足够的丰富,这里我们从内核的角度来分析。虚拟空间的管理是以进程为基础的,每个进程都有各自的虚存空间,除此之外,每个进程的“内核空间”是为所有的进程所共享的。一个进程的虚拟地址空间主要由两个数据结构来描述: mm_struct 和vm_area_struct。
The Memory Descriptor
mm_struct包括进程中虚拟地址空间的所有信息,mm_struct定义在include/linux/mm_types.h:
|
结合mm_struct和下图32位系统典型的虚拟地址空间分布更能直观的理解(来自《深入理解计算机系统》):
Virtual Memory Area
vm_area_struct描述了虚拟地址空间的一个区间, 一个进程的虚拟空间中可能有多个虚拟区间, vm_area_struct同样定义在include/linux/mm_types.h:
|
下图是某个进程的虚拟内存简化布局以及相应的几个数据结构之间的关系:
mmap是什么?
相信做过Linux开发的兄弟都或多或少用过,或者听说过mmap,但可能并不完全了解mmap的作用。
mmap字面上是内存映射的意思,听起来比较抽象,其用法有很多,但总结起来,主要是如下两个用途:
- 将文件内容映射到进程用户态的虚拟地址空间中,如此,进程就可以通过读写相应的虚拟地址空间内容,而直接读写相应文件中的内容。如此映射最大的好处是,进程可以直接从用户态访问文件中的数据,而不需要用户态和内核态之间的内存拷贝操作(正常流程下,如果想要向文件中写数据,需要将数据从用户态拷贝到内核态,然后再从内核态写入文件),相当于少了一次内存拷贝操作,这也是人们常说的零拷贝技术之一。当然,这里的文件不限于普通文件,Unix环境中,一切皆文件嘛,这里的文件完全可能是特殊文件,比如设备文件,那相应的mmap操作就需要单独的驱动实现了。
- 分配内存。当mmap中传入的fd为空时,其作用就是分配内存,类似于malloc(其实malloc的glibc实现中就使用了mmap来分配内存),俗称“匿名映射”,匿名的意思就是fd为空,名字很抽象,本质不复杂:就是在进程的虚拟地址空间中分配一段虚拟内存(用vma表示),物理内存在缺页异常中分配,并修改相应页表。
mmap基本原理
如前面所述,mmap主要有两种用途,其中第一种用途分两种情况(普通文件和特殊文件),这里分别描述相关原理:
1.普通文件的mmap基本原理为:每个文件(file)都定义了相应的文件操作数据结构(file_operations),该结构中定义了mmap操作,比如ext3文件系统文件对应的文件操作为:ext3_file_operations
,对应的mmap操作接口为:generic_file_mmap
。generic_file_mmap
中就是创建(或查找利用现有的)vma,然后设置相应的成员,包括缺页异常对应的处理钩子,最后返回相应的虚拟地址。当进程访问写相应的虚拟地址时,硬件会触发缺页异常(磁盘页未缓存到屋里内存中),此时会进入缺页异常流程(do_page_fault
),然后会进入之前设置的缺页异常钩子,该钩子会触发文件系统的写入操作,最终会将数据写入到文件中。
2.特殊文件(以设备文件为例)的mmap的基本原理与上述普通文件类似,主要差别在于:其定义的文件操作不同,对应的接口不同;其实现取决于具体的驱动,流程与普通文件实现可以完全不同,这里不详述。
3.匿名映射的基本原理:由于没有具体的fd,没有对应的文件,匿名映射没有对应的文件操作,其流程比较直接,主要还是创建(或查找利用现有的)vma,然后设置相应的成员,返回相应的虚拟地址。当进程访问写相应的虚拟地址时,硬件会触发缺页异常(因为相应的页表项还没有创建),此时会进入缺页异常流程(do_page_fault
),然后会进入匿名映射对应的流程,主要就是为虚拟地址范围创建相应的页表,本质上,就是分配了相应的物理内存。
代码流程
mmap流程
mmap有相应的系统调用接口,从系统调用开始的大致流程如下(代码不是很好找,需要仔细看看):
SYSCALL_DEFINE6(mmap_pgoff, ...
sys_mmap_pgoff
SYSCALL_DEFINE6(mmap_pgoff, ...
vm_mmap_pgoff
do_mmap_pgoff
mmap_region
file->f_op->mmap() //不同文件(驱动)自己定义的mmap钩子,比如ext3文件系 统对应为`generic_file_mmap`
文件缺页异常流程
do_page_fault
__do_page_fault
handle_pte_fault
__handle_mm_fault
handle_pte_fault
do_fault
do_shared_fault
__do_fault
vma->vm_ops->fault() //文件系统或驱动注册的缺页异常钩子,如ext4文件对应为filemap_fault
匿名映射缺页异常流程
do_page_fault
__do_page_fault
handle_pte_fault
__handle_mm_fault
handle_pte_fault
do_anonymous_page
mk_pte //创建页表项
set_pte_at
普通文件映射与匿名文件映射的用法
用法举例1
总结
1.对于普通文件的mmap,在执行后仅仅是分配了一个可用的虚拟地址以及缺页处理函数,访问的时候按照缺页处理将文件从磁盘上读入,修改后的内存页最终会回写到磁盘上
2.以字符设备为例,设备文件的大小为0;设备文件的mmap需要自己定义驱动函数来返回一个映射地址。