【xv6】Copy on write fork()
fork() 的缺点
在日常的 Linux 编程中,一般会通过 fork() 创建一个新进程,分为父子进程来处理逻辑;或者在子进程中通过 exec() 来加载一个新程序,shell 启动新程序就是这样操作的。在执行 fork() 时,子进程会完全拷贝父进程,包括其执行代码段、数据段、栈和堆,并在接下来的处理过程中修改数据段等内存空间中的数据,而不影响另一个进程。
然而这样的直接拷贝,对于内存来说是一种浪费。执行 fork() 后子进程可能只是简单的执行了一些动作;或者直接调用 exec() 将新程序替换到代码段,重新初始化数据段和堆栈。那 fork() 时的完整拷贝又有什么意义呢?
Copy on write
基于以上以及其他缘由,开发者们提出了「Copy on write」机制。
在 COW 机制之前,执行 fork() 后会在 kernel 中初始化子进程的数据结构,并逐页复制父进程的物理页块的内容到自己的页块上,并在页表中将虚拟地址映射到物理地址上。
而 COW 机制不会逐页复制父进程的物理页块,而是直接在页表中将虚拟地址映射到父进程的物理地址上,即父子进程共享同一物理地址。同时将物理页块设置为不可读,防止某一进程修改影响到其他进程。当某一个进程需要修改内存空间时怎么办呢?因为进程尝试写一个不可写的物理页块,会触发 Store page fault,可以通过检查物理页块的标志位,确定它是一个 COW 页块,那么就拷贝其内容到一个新申请的物理页块上,将这个新的物理页块映射到需要修改内存的进程中,供其使用。这样父子进程就不会相互影响。
当多个进程使用同一物理页块时,可以通过维护一个引用计数数组,来确定释放物理页块时是假释放还是真销毁。
这种方式避免了无意义的拷贝工作,而是将真正有『价值』的拷贝工作延后到需要的时候,避免了浪费。
结合 COW 的 fork() 实现
具体实现可见 GitHub。
剩余内容已隐藏
【xv6】Copy on write fork()
fork() 的缺点
在日常的 Linux 编程中,一般会通过 fork() 创建一个新进程,分为父子进程来处理逻辑;或者在子进程中通过 exec() 来加载一个新程序,shell 启动新程序就是这样操作的。在执行 fork() 时,子进程会完全拷贝父进程,包括其执行代码段、数据段、栈和堆,并在接下来的处理过程中修改数据段等内存空间中的数据,而不影响另一个进程。
然而这样的直接拷贝,对于内存来说是一种浪费。执行 fork() 后子进程可能只是简单的执行了一些动作;或者直接调用 exec() 将新程序替换到代码段,重新初始化数据段和堆栈。那 fork() 时的完整拷贝又有什么意义呢?
Copy on write
基于以上以及其他缘由,开发者们提出了「Copy on write」机制。
在 COW 机制之前,执行 fork() 后会在 kernel 中初始化子进程的数据结构,并逐页复制父进程的物理页块的内容到自己的页块上,并在页表中将虚拟地址映射到物理地址上。
而 COW 机制不会逐页复制父进程的物理页块,而是直接在页表中将虚拟地址映射到父进程的物理地址上,即父子进程共享同一物理地址。同时将物理页块设置为不可读,防止某一进程修改影响到其他进程。当某一个进程需要修改内存空间时怎么办呢?因为进程尝试写一个不可写的物理页块,会触发 Store page fault,可以通过检查物理页块的标志位,确定它是一个 COW 页块,那么就拷贝其内容到一个新申请的物理页块上,将这个新的物理页块映射到需要修改内存的进程中,供其使用。这样父子进程就不会相互影响。
当多个进程使用同一物理页块时,可以通过维护一个引用计数数组,来确定释放物理页块时是假释放还是真销毁。
这种方式避免了无意义的拷贝工作,而是将真正有『价值』的拷贝工作延后到需要的时候,避免了浪费。
结合 COW 的 fork() 实现
具体实现可见 GitHub。
剩余内容已隐藏