Part B: Copy-on-Write Fork
在本节中,主要讨论 JOS 系统中用户态进程创建子进程。
在此,我们需要了解 fork()函数中用到的 COW 技术。在 fork()系统调用中需要复 制父进程的地址空间到子进程中,所谓复制,只是进程的基本资源的复制,如 task_struct 数据结构、系统空间堆栈、页面表等等。传统的 fork()系统调用直接把所有的资源复制给 新创建的进程。这种实现效率比较低下,因为它拷贝的数据也许并不共享,而且,如果新进 程打算执行一个新的映像,那么所有的拷贝都将前功尽弃。JOS 系统中 fork()使用写时拷 贝(copy-on-write)页实现。
写时拷贝是一种可以推迟甚至免除拷贝数据的技术,即内核此时并不复制整个进程地址 空间,而是让父进程和子进程共享同一个拷贝。只有在需要写入的时候,数据才会被复制, 从而使各个进程拥有各自的拷贝。也就是说,资源的复制只有在需要写入的时候才进行,在 此之前,只是以只读方式共享。这种技术使地址空间上的页的拷贝被推迟到实际发生写入的 时候。在页根本不会被写入的情况下,它们就无需复制了。fork()的实际开销就是复制父 进程的页表以及给子进程创建惟一的进程描述符。在系统中,进程创建后都会马上运行一个 可执行的文件,这种优化可以避免拷贝大量根本就不会被使用的数据。
由于用户态 fork 涉及到页表出错的处理,即当创建的进程引用一个不存在页面中的内 存地址时,就会触发 CPU 产生页错误异常中断,并把引起中断的线性地址放到 CR2 控制寄 存器中。因此处理该中断的过程就可以知道发生页异常的确切地址,从而可以把进程要求的 页面从二级存储空间加载到物理内存中。我们首先要讨论的就是关于用户级别的页错误处 理。