9.7 案例:Intel Core i7/Linux 内存系统
见书576
9.8 Linux虚拟内存系统
与进程相关的数据结构 (比如:页表、task和mm结构、内核栈) | 对每个进程不同 |
物理内存 | 内核虚拟内存 |
内核代码和数据 | 对每个进程一样 |
用户栈 | 进程虚拟内存 |
共享库的内存映射区域 | |
运行时堆(malloc分配) | |
未初始化数据(.bss) | |
已初始化数据(.data) | |
代码(.text) |
每个存在的虚拟页面都保存在某个区域中,这样可以允许虚拟内存有间隙,内核不用记录不存在的虚拟页。
内核为系统中每个进程维护一个单独的任务结构(task_struct)。任务结构中的元素包括或者指向内核运行该进程所需要的全部信息。(PID,指向用户栈的指针,可执行文件的名字,程序计数器等)
mm_struct 描述了虚拟内存的当前状态:
其中pgd指向第一级页表的基址
mmap指向vm_area_structs的链表。每个vm_area_structs都描述了当前虚拟地址空间的一个区域。
vm_area_structs包含以下字段:
vm_start:指向这个区域的起始处
vm_end:指向这个区域的结束处
vm_prot:描述这个区域内包含的所有页的读写许可权限
vm_flags:描述这个区域的页面是和其他区域共享的,还是私有的。
vm_next:指向链表中下一个区域结构。
linux缺页异常处理
- 缺页处理程序会先判断虚拟地址是否合法,通过和每个区域结构中的vm_start和vm_end比较判断,如果是不合法的,就会触发段错误。
- 判断试图进行内存访问是否合法,如果不合法就会触发一个保护异常。
- 选择一个牺牲页面,如果这个页面修改过就交换,换入新的页面并更新页表,然后重启引起缺页中断的指令。
9.8 内存映射
定义:将一个虚拟内存区域和一个磁盘上的对象关联起来,以初始化这个虚拟内存区域的内容的过程被称为内存映射。
虚拟内存区域可以映射到下面两种类型的对象中的一个:
- Linux文件系统中的普通文件:因为按需进行页面调度,所以这些虚拟页面没有实际交换进入物理内存,而是等到CPU第一次引用这个页面(发射一个落在这个页面范围内的虚拟地址)。如果区域比文件大小大,则用零填充剩下区域。
- 匿名文件:匿名文件由内核创建,包含的全是二进制的零。CPU第一次引用这个区域内的虚拟页面之后,内核就换出一个牺牲页面,并用二进制0覆盖并更新页表,然后把这个页面标记为驻留在内存中。注意:磁盘和内存没有实际的数据传输,所以映射到匿名文件中的页面也叫请求二进制零的页。
进程的共享区域和私有区域。
私有对象使用一种叫做写时复制的技术,私有对象开始生命周期的方式基本与共享对象一样,在物理内存中只保存一份私有对象的副本。
每个映射私有对象的进程,相应私有区域的页表条目被标记为只读。区域结构被标记为私有的写时复制,只要没有进程试图写自己的私有区域,它们就继续共享一份物理内存的副本。如果一个进程试图写私有区域的某个页面,就会触发一个保护故障。
故障处理程序会在物理内存中创建这个页面的一个新副本,并更新页表指向这个新副本,然后恢复这个页面的可写权限。
当故障处理程序返回时,CPU重新执行这个写操作,之后在这个新页面上的写操作就可以正常运行了。
注意:是以页面为单位。
图片见书P584