![](/2021/05/30/TheMethodUsedInMM/Linux-List.png)
阅读./mm
中的memory.c
和page.s
的前期了解准备,Linux内存管理中使用的两种机制。
写时复制(Copy on Write)机制
写时复制是一种推迟或免除复制数据的一种方法。 此时内核并不去复制进程整个地址空间中的数据,而是让父进程和子进程共享同一个拷贝。 当进程A使用系统调用fork创建出一个子进程B时,由于子进程B实际上是父进程A的一个拷贝,因此会拥有与父进程相同的物理页面。也即为了达到节约内存和加快创建进程速度的目标,fork()函数会让子进程B以只读方式共享父进程A的物理页面。同时将父进程A对这些物理页面的访问权限也设置为只读(详见memory.c中的copy_page_tables()函数)。这样一来,当父进程A或子进程B任何一方对这些共享物理页面执行写操作时,都会产生页面出错异常(page_fault int 14)中断,此时CPU就会执行系统提供的异常处理函数do_wp_page()来试图解决这个异常,这就是写时复制机制。
do_wp_page()会对这块导致写入异常中断的物理页面进行取消共享操作(使用un_wp_page()函数),并为写进程复制一个新的物理页面,使父进程A和子进程B各自拥有一块内容相同的物理页面。这时才真正地进行了复制操作(只复制这一块物理页面)。并且把将要执行写入操作的这块物理页面标记成可以写访问的。最后,从异常处理函数中返回时,CPU就会重新执行刚才导致异常的写入操作指令,使进程能够继续执行下去。
因此,对于进程在自己的虚拟地址范围内进行写操作时,就会使用上面这种被动的写时复制操作,也即:写操作–>页面异常中断–>处理写保护异常–>重新执行写操作指令。对于系统内核代码,当在某个进程的虚拟地址范围内执行写操作时,例如进程调用某个系统调用,若该系统调用会将数据复制到进程的缓冲区域中,则内核会通过verify_area()函数首先主动地调用内存页面验证函数write_verify(),来判断是否有页面共享的情况存在,如果有,就进行页面的写时复制操作。
另外,值得注意的一点是在Linux 0.11内核中,在内核代码地址空间(线性地址<1MB)执行fork()来创建进程时并没有采用写时复制技术。因此当进程0(idle进程)在内核空间创建进程1(init进程)时将使用同一段代码和数据段。但由于进程1复制的页表项也是只读的,因此当进程1需要执行堆栈(写)操作时也会引起页面异常,从而在这种情况下内存管理程序也会在主内存区(应该就是用户内存)中为该进程分配内存。
由此可见,写时复制把对内存页面的复制操作推迟到实际要进行写操作的时刻,在页面不会被写的情况下就可以根本不用进行页面复制操作。例如,当fork()创建了一个进程后立即调用execve()去执行一个新程序的时候(所以实际上exec函数如果没有涉及到写操作也不会复制内存页面,但exec函数大多数都会涉及写操作)。因此这种技术可以避免不必要的内存页面复制的开销。
需求加载(Load on demand)机制
在使用execve()系统调用加载运行文件系统上的一个执行映像文件时,内核除了在CPU的4G线性地址空间中为对应进程分配了64MB的连续空间,并为其环境参数和命令行参数分配和映射了一定数量的物理内存页面以外,实际上并没有给执行程序分配其他任何物理内存页面。当然也谈不上从文件系统上加载执行映像文件中的代码和数据。因此一旦该程序从设定的入口执行点开始运行就会立刻引起CPU产生一个缺页异常(执行指针所在的内存页面不存在)。此时内核的缺页异常处理程序才会根据引起缺页异常的具体线性地址把执行文件中相关的代码页从文件系统中加载到物理内存页面中,并映射到进程逻辑地址中指定的页面位置处。当异常处理程序返回后CPU就会重新执行引起异常的指令,使得执行程序能够得以继续执行。若在执行过程中又要运行到另一页中还未加载的代码,或者代码指令需要访问还未加载的数据,那么CPU同样会产生一个缺页异常中断,此时内核就又会把执行程序中的其他对应页面内容加载到内存中。就这样,执行文件中只有运行到(用到)的代码或数据页面才会被内核加载到物理内存中。这种仅在实际需要时才加载执行文件中页面的方法被称为需求加载(Load on demand)技术或需求分页(demand-paging)技术。
采用需求加载技术的一个明显优点是在调用execve()系统后能够让执行程序立刻开始运行,而无需等待多次的块设备I/O操作把整个执行文件映像加载到内存中后才开始运行。因此系统对执行程序的加载执行速度将大大地提高。但这种技术对被加载执行目标文件的格式有一定要求。它要求被执行的文件目标格式是ZMAGIC类型的,即需求分页格式的目标文件格式。在这种目标文件格式中,程序的代码段和数据段都从页面边界开始存放,以适应内核以一个页面为单位读取代码或数据内容。
参考资料
《Linux内核完全注释(赵炯 著)》
发布时间: 2021-05-30
最后更新: 2024-05-12
本文标题: Linux内存管理中使用的两种机制
本文链接: https://cloudflare.luhawxem.com/2021/05/30/TheMethodUsedInMM/
版权声明: 本作品采用 CC BY-NC-SA 4.0 许可协议进行许可。转载请注明出处!