fork后子进程与父进程的内存关系

  fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,linux中引入了“写时复制“技术,也就是只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。于是起初我就感到奇怪,子进程的物理空间没有代码,怎么去取指令执行exec系统调用呢?!原来在fork之后exec之前两个进程用的是相同的物理空间(内存区),子进程的代码段、数据段、堆栈都是指向父进程的物理空间,也就是说,两者的虚拟空间不同,但其对应的物理空间是同一个。当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间,如果不是因为exec,内核会给子进程的数据段、堆栈段分配相应的物理空间(至此两者有各自的进程空间,互不影响),而代码段继续共享父进程的物理空间(两者的代码完全相同)。而如果是因为exec,由于两者执行的代码不同,子进程的代码段也会分配单独的物理空间。
       在网上看到还有个细节问题就是,fork之后内核会
通过将子进程放在队列的前面,以让子进程先执行,以免父进程执行导致写时复制,而后子进程执行exec系统调用,因无意义的复制而造成效率的下降。

       详细情况可以看看这位仁兄的文章:
十辨十析之辨一――fork()、写时复制、vfork()

最近几本关于L的书,想来通络一下。也干了近几件疯狂的事情,想想都要偷着自娱自乐一番,真是无聊到尽头了,也就是另一番风景。有些你以前一直巴巴的信仰为真的东西,偶乐改变一下,结果发现,原来不是那个理,换个道也没什么比以前差的,丫,这样,也不错。

打算写十辨十析,罗罗一大堆,写些什么呢,想来想去,还是写些基础的东西吧,希望这些基础的东西对朋友们理解其它方面能有个帮助。这个专题的思想:what ? why ? how ? 总之要从根基上讲清楚,重点在原理上了。对了图作的不精,请大家谅解。

这三个方法是大佬L用 于创建子进程的三种方法。其实理解它们,要有一个实现上的意识即:进程->虚拟地址空间->物理地址空间即真实的存储。用户进程能感知的是进程的虚拟地址 空间,而虚拟地址空间->物理地址空间则由底层的内核来帮你实现。一个进程在地址空间上的表现形式就是:正文段,数据段,堆,栈。嗯,主要就是这四个部 分,内核为其分配相应的数据结构来表示它们,其看做是进程在地址空间的实体,也可以想象为灵魂。随后内核会为这四部分分配相应的载体,即真正的物理存储, 就像灵魂要附之于身体一样,那么,这些物理存储就是进程的真正实体的,我们称之为身体。那么这三个方法有什么不同呢?

ok,现在有一个父进程P1,这是一个主体,那么它是有灵魂也就身体的哦。现在在其虚拟地址空间(有相应的数据结构表示)上有:正文段,数据段,堆,栈这四个部分,相应的,内核要为这四个部分分配各自的物理块。即:正文段块,数据段块,堆块,栈块。至于如何分配,这是内核去做的事,在此不详述。

1.       现在P1用fork()函数为进程创建一个子进程P2,内核:(1)复制P1的正文段,数据段,堆,栈这四个部分,注意是其内容相同。(2)为这四个部分分配物理块,P2的:正文段->PI的正文段的物理块,其实就是不为P2分配正文段块,让P2的正文段指向P1的正文段块,数据段->P2自己的数据段块(为其分配对应的块),堆->P2自己的堆块,栈->P2自己的栈块。如下图所示:同左到右大的方向箭头表示复制内容。



图1


2.       写时复制技术:内核只为新生成的子进程创建虚拟空间结构,它们来复制于父进程的虚拟究竟结构,但是不为这些段分配物理内存,它们共享父进程的物理空间,当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间。




图2

3.       vfork():这个做法更加火爆,内核连子进程的虚拟地址空间结构也不创建了,直接共享了父进程的虚拟空间,当然了,这种做法就顺水推舟的共享了父进程的物理空间。



图3

通 过以上的分析,相信大家对进程有个深入的认识,它是怎么一层层体现出自己来的,进程是一个主体,那么它就有灵魂与身体,系统必须为实现它创建相应的实体, 灵魂实体与物理实体。这两者在系统中都有相应的数据结构表示,物理实体更是体现了它的物理意义。呵呵,说了这么多,其实系统之所以提供这三个方法,也都是 从实现效率上来考虑的,一般fork后要exec,所以很多父进程的数据对于子进程来说都是不需要的,后两种方法就是大佬L区别于Unix的一个主要特征,也可以说是其高明处之一吧,其创建进程特别的高效,怎么高效,通过以上的比较与分析,相信大家也能明白个五六了吧。

猜你喜欢

转载自igaozh.iteye.com/blog/1677969