Linux虚拟内存系统
Linux将进程的地址空间,将虚拟内存区域组成一些区域(段)的集合。一个区域就是已分配的虚拟内存连续片(chunk),这些段通过某种方式相互链接。
一个进程的地址空间由下而上由多个不同类型的连续区域(段)组合而成:代码段、初始化数据段、未初始化数据段、运行时堆区域、共享库的映射区域、运行时栈区域、内核代码和数据段(多个进程通过虚拟内存方式共享)、内核为每个进程维护的元数据(页表、task_struct、mm结构)
它们在虚拟地址空间中内部连续,中间则存在空隙,内核通过链表(早期)来记录这些段的位置信息,访问权限等
task_struct中保存有内核运行进程的基本信息,PID、用户进程栈指针、程序计数器PC等
Linux采取链表组织多个段区域
task_struct结构中mm_struct结构用于维护进程的虚拟内存空间,其核心是mmap结构,即一个维护每个虚拟内存段的链表,每一个vm_area_struct结构记录了一个虚拟内存段的基本信息。
vm_strat | 指向这个区域的起始位置 |
---|---|
vm_end | 指向这个区域的结束位置 |
vm_prot | 区域的访问权限,如代码段只读 |
vm_flag | 一些标记位,进程是否私有或共享 |
vm_next | 指向链表中的下一个区域结构 |
Linux缺页异常处理
MMU内存管理单元在引用一个虚拟地址时,会执行一个前置判断,其基本过程如下:
①虚拟地址A位置是否合法,即地址A是否在某个合法的区域内,其具体的做法是遍历每个区域结构,判断地址A是否在某个区域的vm_start和vm_end之间。如果不再某个区域内,触发一个段访问错误。
虚拟地址空间可能包含很多这样的区域,链表遍历查找会很耗时,Linux在链表基础之上实现了一个查找树,并借此提高查找速度。
②试图访问的内存是否有合法的权限,进程是否有读写该区域的权限。例如运行在用户模式下的进程无法对代码段执行写操作,否则触发一个保护异常。
③若一切顺利,内核确认对地址A的引用存在且合法,那么执行页面置换逻辑,将相应的虚拟页置换进主内存中,缺页中断完成后,MMU即可对该虚拟内存正常的引用。