2014年5月24日 星期六

Linux-C語言-fork() V.S vfork()

先來英文的解釋

Linux description
       vfork(), just like fork(2), creates a child process of the calling
       process.  For details and return value and errors, see fork(2).

       vfork() is a special case of clone(2).  It is used to create new
       processes without copying the page tables of the parent process.  It
       may be useful in performance-sensitive applications where a child is
       created which then immediately issues an execve(2).

       vfork() differs from fork(2) in that the calling thread is suspended
       until the child terminates (either normally, by calling _exit(2), or
       abnormally, after delivery of a fatal signal), or it makes a call to
       execve(2).  Until that point, the child shares all memory with its
       parent, including the stack.  The child must not return from the
       current function or call exit(3), but may call _exit(2).

       As with fork(2), the child process created by vfork() inherits copies
       of various of the caller's process attributes (e.g., file
       descriptors, signal dispositions, and current working directory); the
       vfork() call differs only in the treatment of the virtual address
       space, as described above.

       Signals sent to the parent arrive after the child releases the
       parent's memory (i.e., after the child terminates or calls
       execve(2)).

參考資料
http://man7.org/linux/man-pages/man2/vfork.2.html



中文的解釋
fork()和vfork()這兩個系統功能都可以複製出和呼叫者﹙parent﹚完全相同的process﹙child﹚,但呼叫vfork()後的parent process會被暫停,直到被複製出來的child process執行了exec()或exit();而呼叫fork()後的parent process會和新產生的child process平行﹙concurrent﹚執行。
接下來我們必須約略解釋一下fork()在Linux中的實現方式,旨在讓讀者知道為什麼這個系統功能沒法直接移植到沒有MMU的CPU上;首先我們必須先介紹一下”copy-on-write”這個觀念:
一個程式在執行時會佔據記憶體空間,粗略可分為程式段、資料段、堆疊段與常數段,其中程式段與堆疊段是唯讀的,資料段與堆疊段的內容則有可能在執行時期被改變。在Linux中,當某個process呼叫fork()產生child process時,系統只會為新的process配置堆疊段,其他的記憶體區段都是共用的;實際上在child process呼叫exec()去執行另一個程式前,諸如程式段以及常數段這些內容不可以被改變的記憶體區段始終可以共用。可是資料段就不能一直共用,如果parent與child process同時去操作某個變數勢必會引起混亂。
Child process被fork出來後馬上呼叫exec()去執行其他程式是最常用的流程,以此說來,雖然每個process都必須有獨立的資料段,但馬上為child process配置資料段是很不經濟的,因為在大部分的狀況下child process並不會去對資料段作寫入的動作,在執行exec()後,之前的資料段就沒用了。
為了解決這個問題,Linux(fork)採用”copy-on-write”的技術,在child process尚未對資料段作寫入的動作之前,parent與child process共用資料段;當child process對資料段記憶體作出寫入的要求時,系統會配置一塊實體記憶體﹙一個page﹚給child process,並將原本資料段中被要求寫入之page的內容複製到這塊新的page;接著系統會更改child process的page table,使要被寫入資料的虛擬位址可以對應到上述新配置的實體記憶體位址。
此時child process與parent process的資料段大部分都還是功用的,不同的地方只是這次要被寫入的page;這種演算法的好處很多,在最節省記憶體的前提下使得parent與child process不致互相影響。要達到這種效果,CPU沒有支援MMU是做不到的,所以uClinux無法直接支援fork()這個系統功能。
參考資料
Read more: http://tw.tonytuan.org/2009/04/vforkuclinux.html#ixzz32cpA9Qoi




還有更簡單的區別
1. fork: child process 會拷貝 parent process 的數據段
    vfork: child process 會與 parent process共用數據段 (應該就是上面中文寫的同一塊記憶體)

2. fork: parent process和child process的執行順序不確定
    vfork: child process會先執行,等執行完後, parent process再執行



Example: vfork1.c

#include <unistd.h>
#include <stdio.h>

int main(void)
{
   pid_t pid;
   int count=0;

   pid=vfork();

   count++;
   printf("count%d\n", count);

   exit(0);
   return 0;
}

執行結果:
>gcc rfork1.c -o rfork1
>./rfork1
count=1
count=2


Note:

  1. 如果將程式中 vfork 改為 fork,則執行結果 兩個count都會為1. (因為vfork 的child和parent會共用記憶體,而fork則是各自有一塊記憶體)
  2. 因為使用vfork, child process執行完後,才會執行parent process,所以城市中要加入"exit(0);", 用來確定process有執行完畢.不加這行child process可能不會結束,parent process就也就不會開始,這樣執行程式時,就會出現錯誤!

沒有留言:

張貼留言