一篇读懂Linux内核-内核地址空间分布和进程地址空间( 三 )


在上面的vm_flags域中存放的是VMA标志,标志了内存区域所包含的页面的行为和信息,反映了内核处理页面所需要遵循的行为准则,如下表下述:

一篇读懂Linux内核-内核地址空间分布和进程地址空间

文章插图
 
上表已经相当详细了,而且给出了说明,我就不说了 。在vm_area_struct结构体中的vm_ops域指向域指定内存区域相关的操作函数表,内核使用表中的方法操作VMA 。vm_area_struct作为通用对象代表了任何类型的内存区域,而操作表描述针对特定的对象实例的特定方法 。操作函数表由vm_operations_struct结构体表示,定义在linux/mm.h中,如下:
struct vm_operations_struct { void (*open) (struct vm_area_struct *); void (*close) (struct vm_area_struct *); struct page * (*nopage) (struct vm_area_struct *, unsigned long, int); int (*populate) (struct vm_area_struct *, unsigned long, unsigned long,pgprot_t, unsigned long, int); };
open:当指定的内存区域被加入到一个地址空间时,该函数被调用 。close:当指定的内存区域从地址空间删除时,该函数被调用 。nopages:当要访问的页不在物理内存中时,该函数被页错误处理程序调用 。populate:该函数被系统调用remap_pages调用来为将要发生的缺页中断预映射一个新映射 。
 
记性好的你一定记得内存描述符中的mmap和mm_rb域都独立地指向与内存描述符相关的全体内存区域对象 。它们包含完全相同的vm_area_struct结构体的指针,仅仅组织方式不同而已 。前者以链表的方式进行组织,所有的区域按地址增长的方向排序,mmap域指向链表中第一个内存区域,链中最后一个VMA结构体指针指向空 。而mm_rb域采用红--黑树连接所有的内存区域对象 。它指向红--黑输的根节点 。地址空间中每一个vm_area_struct结构体通过自身的vm_rb域连接到树中 。关于红黑二叉树结构我就不细讲了,以后可能会详细说这个问题 。内核之所以采用这两种结构来表示同一内存区域,主要是链表结构便于遍历所有节点,而红黑树结构体便于在地址空间中定位特定内存区域的节点 。我么可以使用/proc文件系统和pmap工具查看给定进程的内存空间和其中所包含的内存区域 。这里就不细说了 。
内核也为我们提供了对内存区域操作的API,定义在linux/mm.h中:
 
记性好的你一定记得内存描述符中的mmap和mm_rb域都独立地指向与内存描述符相关的全体内存区域对象 。它们包含完全相同的vm_area_struct结构体的指针,仅仅组织方式不同而已 。前者以链表的方式进行组织,所有的区域按地址增长的方向排序,mmap域指向链表中第一个内存区域,链中最后一个VMA结构体指针指向空 。而mm_rb域采用红--黑树连接所有的内存区域对象 。它指向红--黑输的根节点 。地址空间中每一个vm_area_struct结构体通过自身的vm_rb域连接到树中 。关于红黑二叉树结构我就不细讲了,以后可能会详细说这个问题 。内核之所以采用这两种结构来表示同一内存区域,主要是链表结构便于遍历所有节点,而红黑树结构体便于在地址空间中定位特定内存区域的节点 。我么可以使用/proc文件系统和pmap工具查看给定进程的内存空间和其中所包含的内存区域 。这里就不细说了 。内核也为我们提供了对内存区域操作的API,定义在linux/mm.h中:
 
接下来要说的两个函数就非常重要了,它们负责创建和删除地址空间 。
内核使用do_mmap()函数创建一个新的线性地址空间 。但如果创建的地址区间和一个已经存在的地址区间相邻,并且它们具有相同的访问权限的话,那么两个区间将合并为一个 。如果不能合并,那么就确实需要创建一个新的vma了,但无论哪种情况,do_mmap()函数都会将一个地址区间加入到进程的地址空间中 。这个函数定义在linux/mm.h中,如下:
unsigned long do_mmap(struct file *file, unsigned long addr, unsigned long len, unsigned long prot,unsigned long flag, unsigned long offset)
这个函数中由file指定文件,具体映射的是文件中从偏移offset处开始,长度为len字节的范围内的数据,如果file参数是NULL并且offset参数也是0,那么就代表这次映射没有和文件相关,该情况被称作匿名映射 。如果指定了文件和偏移量,那么该映射被称为文件映射(file-backed mapping),其中参数prot指定内存区域中页面的访问权限,这些访问权限定义在asm/mman.h中,如下:
一篇读懂Linux内核-内核地址空间分布和进程地址空间

文章插图
 
flag参数指定了VMA标志,这些标志定义在asm/mman.h中,如下:


推荐阅读