这几天看了一点进程的知识,也会创建一个或几个子进程了,在这里,我很想和大家分享一下,在创建子进程的时候,我们现在所说的写时拷贝技术。
我们知道,在我们fork一个进程之后,系统就会给我们创建一个子进程,此时,子进程会继承父进程几乎所有的资源,并且系统会给子进程分配一个新的物理空间,但实际上,子进程和父进程是用的同一个代码段的,在fork之后子进程只是复制了父进程的数据段,堆栈段,但即使这样,也还是会浪费系统的资源空间,之后,fork做了这样的改变,父进程在fork完之后,子进程只是有了自己的独立虚拟空间,它的物理空间其实是和父进程共享的,当系统检测到父进程和子进程的相应段发生变化的时候,也就是有写的操作的时候,系统才给子进程分配相应的物理空间。
我们也许会问,为什么有写时拷贝这样的技术,这样做的用处是什么,你可以想想,当我们fork一个程序之后,如果立马使用了exec函数族,那么如果还像以前那样,fork完之后就复制父进程的资源给子进程,但是子进程根本用不到这些代码和数据,那么系统的资源就会被浪费,unix和unix(like)是非常讲究效率的操作系统,是决不会允许这样的事情发生的,所以,写时复制可以很大的节约系统的资源。
现在,我们来比较一下普通的fork和写时拷贝技术的区别:
fork:复制父进程几乎所有的资源,代码段,数据段,堆栈段,内容相同,然后给子进程分配物理空间,子进程的代码段和父进程共享物理空间,但数据段和堆栈段都是系统给重新分配的物理空间写时拷贝:只为新生成的子进程创建虚拟空间结构,它们复制于父进程的虚拟空间结构,但是不为这些段分配物理内存,它们共享父进程的物理空间,当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间。
为了方便大家的理解,我附一张图,帮助大家理解:
注:在上图中p1代表父进程,p2代表子进程,大箭头代表子进程的各个段是从父进程的哪里复制到子进程的哪里,小箭头分别表示父进程和子进程的各个段在物理空间上的关系,如图,我们可以看到子进程和父进程在父进程的物理空间上共享了代码段,这就是写时拷贝
最后,我们再谈一下vfork这个神奇的东西。
我们知道,用vfork创建的子进程和父进程是共享物理空间的,但vfork用的方法相比前两个更是可怕,因为vfork它直接省略了虚拟空间的创建,同时共享了父进程的虚拟空间和物理空间
总结:传统的fork()系统调用直接把所有的资源复制给新创建的进程。这种实现过于简单并且效率低下,因为它拷贝的数据也许并不共享,更糟的情况是,如果新进程打算立即执行一个新的映像,那么所有的拷贝都将前功尽弃。Linux的fork()使用写时拷贝(copy-on-write)页实现。写时拷贝是一种可以推迟甚至免除拷贝数据的技术。内核此时并不复制整个进程地址空间,而是让父进程和子进程共享同一个拷贝。只有在需要写入的时候,数据才会被复制,从而使各个进程拥有各自的拷贝。也就是说,资源的复制只有在需要写入的时候才进行,在此之前,只是以只读方式共享。