进程地址空间
在Linux中,所有进程之间都以虚拟方式共享内存。
地址空间
进程地址空间由进程可寻址的虚拟内存组成,使得每个进程都能有一个32bit或者64bit的连续地址空间。
进程只能访问有效内存内的物理内存区域,如果访问非法的内存区域,内核将会终止该进程,返回段错误。
内存区域可以包含:
- text section;
- data section;
- 未初始化的全局变量,bss段;
- 进程空间栈;
- 内存映射文件,包括匿名的内存映射,比如由malloc()分配的内存;
内存描述符
内核使用内存描述符结构体来表示进程的地址空间——mm_struct。
1 | struct mm_struct { |
在该结构体中,mm_users记录着使用该地址的进程数目,而mm_count则是mm_struct的主引用计数。比如有9个线程共享改地址,那么mm_users为9,mm_count为1。当mm_count为0时,需要销毁该结构体。
mmap和mm_rb描述相同的对象,该地址空间的全部内存区域,但前者是以链表形式存储,后者则是红黑树形式。一种用来高效遍历,另一种则是为了查询特定内存。
所有的mm_struct都通过自身mmlist连接在一个双向链表中。
分配内存描述符
在task_struct中的mm域存放着该进程的内存描述符,如果父子进程共享内存,需要设置CLONE_VM标识,这样的进程称作线程,在Linux内核中,线程就是共享资源的进程。
1 | if (clone_flags & CLONE_VM){ |
撤销内存描述符
进程退出时,内核会调用exit_mm()函数,进程撤销工作。
mm_struct与内核线程
内核线程没有进程地址空间,也没有内存描述符,所以内核线程的mm域为空。
当一个进程被调度时,该进程的mm域指向的地址空间会被装载到内存。
虚拟内存区域
内存区域由vm_area_struct结构体描述,描述了指定地址空间内连续区间的一个独立内存范围。每个VMA对其mm_struct来说是唯一的。
1 | /* |
VMA标志
VMA是一种位标志,包含在vm_flags内,标记了该内存区域所包含的页面的行为和信息,不同于物理访问权限,反应的是内核处理要遵守的行为标准。
比如可读,写,执行,可共享
VMA操作
vm_area_struct结构体中的vm_ops域指向与指定内存区域相关的操作函数表:
1 |
|
- 指定内存区域被加入到一个地址空间时,open函数被调用
- 指定内存区域被删除时,close函数被调用
内存区域的树型结构和内存区域的链表结构
mmap域使用单独链表连接所有的内存区域对象,每个vm_area_struct结构体通过自身vm_next域连入链表。
而mm_rb则是用红黑树连接所有的内存区域。
操作内存区域
内核经常需要在某个内存区域上执行一些操作。
find_vma()
1 | struct vm_area_struct * find_vma(strcut mm_struct *mm, unsigned long addr); |
该函数在指定的地址空间中搜搜第一个vm_end大于addr的内存区域,如果找不到则返回NULL。并且该返回结果会被缓存在mmap_cache中,查找时会先从缓存找,搜不到则通过红黑树搜索。
find_vma_prev()
该函数返回第一个小于addr的内存区域。
find_vma_intersection()
该函数返回第一个和指定地址区间相交的VMA。
mmap()和do_mmap():创建地址区间
内核使用do_mmap()创建一个新的线性地址区间,不过不同的是,如果新创建的地址区间与一个已经存在的地址区间相邻,并且它们具有相邻的访问权限时,两个区间就会合并为一个。
1 | static inline unsigned long do_mmap(struct file *file, unsigned long addr, |
该函数映射由file指定的文件。
mummap()和do_mummap():删除地址区间
1 | int do_mummap(struct mm_struct *mm, unsigned long start, size_t len); |
该函数从特定的进程地址空间中删除从地址start开始,长度为len字节的空间。
而mummap()则是对do)mummap()函数的一个简单封装。
页表
应用程序虽然面对的是虚拟内存,但真实操作的是物理内存,因此需要一个从虚拟内存到物理内存的转化机制。
在Linux中,是通过三级页表来实现的,顶级页表是页全局目录——PGD,包含了一个pgd_t的数组;而二级页表则是中间页目录——PMD,是一个pmd_t数组;最后一级则是页表项指向物理内存的页表。
为了提高效率,还引入了一个TLB的高速缓存器,在查询物理地址时首先在TLB中查询,找不到时采取内存中查找页表。