首先,这篇博客要解决的几个问题:
1.什么是写时拷贝技术?
2.fork()在复制进程的时候,共享哪些东西?拷贝了哪些东西?
先引入两个概念:
逻辑地址(进程地址空间): cpu所生成的地址。cpu产生的逻辑地址分为:p(页号)它包含在每个页在物理内存中的基址,用来做页表的索引;d(页偏移),同基址相结合,用来确定送入内存设备的物理内存地址。
物理地址:内存单元所看到的地址。
用户程序看不到物理地址。用户只生成逻辑地址。逻辑地址与物理地址呈现一一映射的关系。
写时拷贝
简单来说,fork()只会复制父进程的虚拟地址空间,共享父进程的物理地址,只要发生写操作的时候(因为共享的页会标记为只读,尝试写的时候会引发缺页异常,才会调用相应的函数为子进程开辟自己该段的物理页),才会为子进程开辟新的物理页。
注:(vfork()连虚拟地址内核都不分配,全部共享父进程的。)
传统的fork()系统调用直接把所有的资源复制给新创建的进程。这种实现过于简单并且效率低下,因为它拷贝的数据也许并不共享,更糟的情况是,如果新进程打算立即执行一个新的映像,那么所有的拷贝都将前功尽弃。Linux 的fork(使用写时拷贝(copy-on-write) 页实现。
写时拷贝是-种可以推迟甚至免除拷贝数据的技术。内核此时并不复制整个进程地址空间,而是让父进程和子进程共享同-一个拷贝。 只有在需要写人的时候,数据才会被复制,从而使各个进程拥有各自的拷贝。也就是说,资源的复制只有在需要写人的时候才进行,在此之前,只是以只读方式共享。这种技术使地址空间上的页的拷贝被推迟到实际发生写入的时候才进行。在页根本不会被写入的情况下(举例来说,fork()后立即调用exec()它们就无须复制了。
fork()的实际开销就是复制父进程