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就也就不會開始,這樣執行程式時,就會出現錯誤!

Linux-C語言-fork()

這似乎要從這邊開始解釋


什麼是程序 (process)
在 Linux 系統當中:『觸發任何一個事件時,系統都會將他定義成為一個程序,並且給予這個程序一個 ID ,稱為 PID,同時依據啟發這個程序的使用者與相關屬性關係,給予這個 PID 一組有效的權限設定。』 從此以後,這個 PID 能夠在系統上面進行的動作,就與這個 PID 的權限有關了!


程序與程式 (process & program)
我們如何產生一個程序呢?其實很簡單啦,就是『執行一個程式或指令』就可以觸發一個事件而取得一個 PID 囉!我們說過,系統應該是僅認識 binary file 的,那麼當我們要讓系統工作的時候,當然就是需要啟動一個 binary file 囉,那個 binary file 就是程式 (program) 啦!

使用者 -------->(執行)磁碟中的程式----->(載入記憶體)

記憶體中程序產生的相關資料包含 PID+執行者的權限屬性參數+程式所需程式碼與相關資料. 這應該是由OS所管理和產生的

fork是UNIX一個系統呼叫(system call),process fork時,會複製一個跟自己完全一模一樣的process (with different pid),並利用系統呼叫完成之傳回值,來區分parent process 與child process,而分別賦予child process不同的功能



基本宣告:
    pid_t ford(void) // creat child process

    RETURN VALUE
    On success, the PID of the child process in returned in the parent, and 0 is returned in the child. On failure, -1 is returned in the parent, no child process is created, and errno is set appropriately.
    果然還是英文解釋比較簡單明瞭,意思就是process執行成功之後,回傳的值的類型....
    1. 如果是child process執行成功,就回傳 0
    2. 如果是parent process執行成功,就回傳child process的PID
    3. 如果出現錯誤(大概是指有process執行錯誤或是產生child process失敗吧!),則回傳的值為 -1



Example: fork1.c

#include <unistd.h>   // 要宣告這個 header file,這樣 pid_t 才會被認得
#include <stdio.h>

main()
{
   pid_t pid_test;
   pid_test=fork();  // 執行完這行,就會產生出一個child process

   //這時就有parent process和child process同時在運行
   if (pid_test < 0)            //表示有錯誤發生
      printf("error in fork!");
   else if (pid_test == 0) // 表示child process執行成功
      printf("I am the child process, ID is %d\n", getpid());
   else                              // 表示parent process執行成功
      printf("I am the parent process, ID is %d\n", getppid());

}



執行結果:
>gcc fork1.c -o fork1
I am the parent process, ID is 3403
I am the child process, ID is 11571



Note:
  1. 這只是基本解釋,至於fork在linux中的真正作用為何,還需要找資料來了解和補充

參考資料:
1. 鳥哥的Linux私房菜
http://linux.vbird.org/linux_basic/0440processcontrol.php

Linux-C語言-取得Process ID

取得PROCESS ID

基本宣告:

    pid_t getpid(void) // 取得process ID

    pid_t getppid(void) //取得parent process ID



Example: getpid.c

#include <stdio.h>

int main(void)
{
   printf("PID=%d\n", getpid()):
   printf("PPID=%d\n", getppid()):
}



執行結果:
>gcc getpid.c -o getpid
PID=11418
PPID=3403


Note:
  1. getpid()回傳的資料為"pid_t"的型態,應該是一個整數值
  2. 在ubuntu下的gcc測試,只需include stdio.h就可以執行,不需要其他的header file