fork()函数 —— 父子进程资源

fork()函数功能——建立新进程html

一、父子进程有独立的数据段、堆、栈,共享代码段linux

  Linux中每一个进程都有4G的虚拟地址空间(独立的3G用户空间和共享的1G内核空间),fork()建立的子进程也不例外。子进程资源的由来:函数

  一、1G内核空间既然是全部进程共享,所以fork()建立的子进程天然也将拥有;spa

  二、3G的用户空间是从父进程进程而来。操作系统

  fork()建立子进程时继承了父进程的数据段、代码段、栈段、堆,注意从父进程继承来的是虚拟地址空间,同时也复制了页表(没有复制物理块)。所以,此时父子进程拥有相同的虚拟地址,映射的物理内存也是一致的(独立的虚拟地址空间,共享父进程的物理内存)。.net

  因为父进程和子进程共享物理页面,内核将其标记为“只读”(相似mmap)的private的方式),父子双方均没法对其修改。不管父进程和子进程什么时候试图对一个共享的页面执行写操做,就产生一个错误,这时内核就把这个页复制到一个新的页面给这个进程,并标记为可写,同时修改页表,把原来的只读页面标记为“可写”,留给另一个进程使用——写时复制技术。htm

  注意:内核在为子进程分配物理内存时,并无将代码段对应的数据另外复制一份给子进程,最终父子进程代码段映射的是同一块物理内存(代码段在单个进程内部原本就是只读的)。  blog

  每一个进程的虚拟地址空间均可以是0到4G,只不过其中只有一部分有权访问,每一个进程能够有不一样的映射。两次运行同一个程序就是使用的相同的虚拟地址,可是映射到的物理地倒是不同的。每一个进程都有本身的虚拟地址空间,不一样进程的相同的虚拟地址显然能够对应不一样的物理地址。所以地址相同(虚拟地址)而值不一样没什么奇怪。继承

写时复制技术参见:http://www.cnblogs.com/wuchanming/p/4495479.html队列

二、一次调用两次执行

  参见:http://blog.csdn.net/shenwansangz/article/details/39184789

三、 竞争条件

  Linux是一个多用户操做系统,在同一时间会有许多的用户在争夺系统的资源.有时进程为了早一点完成任务就建立子进程来争夺资源. 一旦子进程被建立,父子进程一块儿从fork处继续执行,相互竞争系统的资源.有时候咱们但愿子进程继续执行,而父进程阻塞,直到子进程完成任务.这个时候咱们能够调用wait或者waitpid系统调用.

  对子进程来讲,fork返回给它0,但它的pid绝对不会是0;之因此fork返回0给它,是由于它随时能够调用getpid()来获取本身的pid;fork以后父子进程除非采用了同步手段,不然不能肯定谁先运行,也不能肯定谁先结束。认为子进程结束后父进程才从fork返回的,这是不对的,fork不是这样的,vfork才这样。

四、fork以后跟随exec

  fork()会产生一个和父进程彻底相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,linux中引入了“写时复制“技术,也就是只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。因为写时复制,在fork以后exec以前两个进程有独立的虚拟地址空间,共享物理内存。只有其中一方须要写操做时,再为子进程的数据段、栈、堆分配物理空间。但在子进程上调用exec时,会清空栈、堆,以及和父进程共享的空间,从新加载新的代码段,这样避免了“写时复制”拷贝共享页面的机会,父进程也同时独自拥有了原来共享的物理内存(可对其读写操做)。

  fork出来子进程以后,父子进程哪一个先调度直接决定了是否须要拷贝的问题?内核通常会先调度子进程,由于不少状况下子进程是要立刻执行exec,而避免无用的复制。若是父进程先调度极可能写共享页面,会产生“写时复制”的无用功。因此,通常是子进程先调度滴。

  在网上看到还有个细节问题就是,fork以后内核会经过将子进程放在队列的前面,以让子进程先执行,以避免父进程执行致使写时复制,然后子进程执行exec系统调用,因无心义的复制而形成效率的降低。

  若是不是由于exec,子进程执行时极可能修改内存,内核会给子进程的数据段、堆栈段分配相应的物理空间(至此二者有各自的进程空间,互不影响),而代码段继续共享父进程的物理空间(二者的代码彻底相同)。而若是是由于exec,因为二者执行的代码不一样,子进程的代码段也会分配单独的物理空间。

 

五、fork()函数的实现过程

  http://blog.csdn.net/shenwansangz/article/details/39184789