Linux系統程式設計(8)—— 程式之程式控制函式fork

尹成發表於2014-07-25

fork()函式通過系統呼叫建立一個與原來程式幾乎完全相同的程式,也就是兩個程式可以做完全相同的事,但如果初始引數或者傳入的變數不同,兩個程式也可以做不同的事。

一個程式呼叫fork()函式後,系統先給新的程式分配資源,例如儲存資料和程式碼的空間。然後把原來的程式的所有值都複製到新的新程式中,只有少數值與原來的程式的值不同。相當於克隆了一個自己。

 

 

函式原型

#include<unistd.h>
#include<sys/types.h>
 
pid_t fork( void);

 

返回值:若成功呼叫一次則返回兩個值,子程式返回0,父程式返回子程式ID;否則,出錯返回-1(pid_t 是一個巨集定義,其實質是int 被定義在#include<sys/types.h>中)

 

函式說明:

 一個現有程式可以呼叫fork函式建立一個新程式。由fork建立的新程式被稱為子程式(child process)。fork函式被呼叫一次但返回兩次。兩次返回的唯一區別是子程式中返回0值而父程式中返回子程式ID。

子程式是父程式的副本,它將獲得父程式資料空間、堆、棧等資源的副本。注意,子程式持有的是上述儲存空間的“副本”,這意味著父子程式間不共享這些儲存空間。

UNIX將複製父程式的地址空間內容給子程式,因此,子程式有了獨立的地址空間。在不同的UNIX (Like)系統下,我們無法確定fork之後是子程式先執行還是父程式先執行,這依賴於系統的實現。所以在移植程式碼的時候我們不應該對此作出任何的假設。

那麼為什麼fork會返回兩次?由於在複製時複製了父程式的堆疊段,所以兩個程式都停留在fork函式中,等待返回。因此fork函式會返回兩次,一次是在父程式中返回,另一次是在子程式中返回,這兩次的返回值是不一樣的。

 

fork呼叫的一個奇妙之處就是它僅僅被呼叫一次,卻能夠返回兩次,它可能有三種不同的返回值:

1、在父程式中,fork返回新建立子程式的程式ID;

2、在子程式中,fork返回0;

3、如果出現錯誤,fork返回一個負值。

 

在fork函式執行完畢後,如果建立新程式成功,則出現兩個程式,一個是子程式,一個是父程式。在子程式中,fork函式返回0,在父程式中,fork返回新建立子程式的程式ID。我們可以通過fork返回的值來判斷當前程式是子程式還是父程式。

引用一位網友的話來解釋fork函式返回的值為什麼在父子程式中不同。“其實就相當於連結串列,程式形成了連結串列,父程式的fork函式返回的值指向子程式的程式id, 因為子程式沒有子程式,所以其fork函式返回的值為0.

呼叫fork之後,資料、堆疊有兩份,程式碼仍然為一份但是這個程式碼段成為兩個程式的共享程式碼段都從fork函式中返回,箭頭表示各自的執行處。當父子程式有一個想要修改資料或者堆疊時,兩個程式真正分裂。

  

fork()在Linux系統中的返回值是沒有NULL的.


出錯返回錯誤資訊(Error Codes)如下:

EAGAIN

達到程式數上限.

ENOMEM

沒有足夠空間給一個新程式分配.

fork出錯可能有兩種原因:

  1)當前的程式數已經達到了系統規定的上限,這時errno的值被設定為EAGAIN。

  2)系統記憶體不足,這時errno的值被設定為ENOMEM。

  

fork函式的特點概括起來就是“呼叫一次,返回兩次”,在父程式中呼叫一次,在父程式和子程式中各返回一次。

fork的另一個特性是所有由父程式開啟的描述符都被複制到子程式中。父、子程式中相同編號的檔案描述符在核心中指向同一個file結構體,也就是說,file結構體的引用計數要增加。

 

下面是一個例子:

#include <unistd.h> 
#include <sys/types.h> 
#include <stdio.h> 
int main(void) 
{ 
   pid_t pid; 
   pid=fork(); 
   switch (pid) 
   { 
   case -1: 
       perror("fork error"); 
       exit(1); 
   case 0: 
       printf("I am the child process, my process id is %d/n",getpid()); 
       break; 
   default: 
       printf("I am the parent process, my process id is %d/n",getpid()); 
       break; 
   } 
    return 0; 
}

 

  建立新程式成功後,系統中出現兩個基本完全相同的程式,這兩個程式執行沒有固定的先後順序,哪個程式先執行要看系統的程式排程策略。

  每個程式都有一個獨特(互不相同)的程式識別符號(process ID),可以通過getpid()函式獲得,還有一個記錄父程式pid的變數,可以通過getppid()函式獲得變數的值。

  fork執行完畢後,出現兩個程式,

為什麼兩個程式的內容完全一樣,但是列印的結果不一樣呢,那是因為判斷條件的原因,上面列舉的只是程式的程式碼和指令,還有變數。

  執行完fork後,程式1的變數為count=0,fpid!=0(父程式)。程式2的變數為count=0,fpid=0(子程式),這兩個程式的變數都是獨立的,存在不同的地址中,不是共用的,這點要注意。可以說,我們就是通過fpid來識別和操作父子程式的。iude

  還有人可能疑惑為什麼不是從#include處開始複製程式碼的,這是因為fork是把程式當前的情況拷貝一份,執行fork時,程式已經執行完了int count=0;fork只拷貝下一個要執行的程式碼到新的程式。(自己新增:因為FORK是複製產生一個新的程式,因此新的程式與舊的的程式之間的上下文,如暫存器上下文等是一致的,也就是說兩個程式的變數值,PC指標值也是一樣的,因此兩個程式都是在同一個位置開始執行)

 

相關文章