複製鏈接
請複製以下鏈接發送給好友

fork

(函數)

鎖定
復刻(英語:fork,又譯作派生分支)是UNIX或類UNIX中的分叉函數,fork函數將運行着的程序分成2個(幾乎)完全一樣的進程,每個進程都啓動一個從代碼的同一位置開始執行的線程。這兩個進程中的線程繼續執行,就像是兩個用户同時啓動了該應用程序的兩個副本。
從一個軟件包拷貝了一份源代碼然後在其上進行獨立的開發,創建不同的軟件。這個術語不只意味着版本控制上的分支,同時也意味着開發者社區的分割,是一種形式的分裂
自由及開放源代碼軟件可以從原有開發團隊復刻而不需要事先的許可,這也不會違反任何著作權法律。授權的專有軟件(例如Unix)的復刻也時有發生。
中文名
復刻
外文名
fork
類    型
函數
應    用
計算機
隸    屬
UNIX
進程類別
父進程

fork介紹

fork系統調用用於創建一個新進程,稱為子進程,它與進程(稱為系統調用fork的進程)同時運行,此進程稱為父進程。創建新的子進程後,兩個進程將執行fork()系統調用之後的下一條指令。子進程使用相同的pc(程序計數器),相同的CPU寄存器,在父進程中使用的相同打開文件。
它不需要參數並返回一個整數值。下面是fork()返回的不同值。
負值:創建子進程失敗。
:返回到新創建的子進程。
正值:返回父進程或調用者。該值包含新創建的子進程的進程ID [1] 

fork分叉函數

頭文件
#include<unistd.h>/*#包含<unistd.h>*/
#include<sys/types.h>/*#包含<sys/types.h>*/

fork函數原型

pid_t fork( void);
(pid_t 是一個宏定義,其實質是int 被定義在#includesys/types.h>中)
返回值: 若成功調用一次則返回兩個值,子進程返回0,父進程返回子進程ID;否則,出錯返回-1

fork函數説明

一個現有進程可以調用fork函數創建一個新進程。由fork創建的新進程被稱為子進程(child process)。fork函數被調用一次但返回兩次。兩次返回的唯一區別是子進程中返回0值而父進程中返回子進程ID。
子進程是父進程的副本,它將獲得父進程數據空間、堆、棧等資源的副本。注意,子進程持有的是上述存儲空間的“副本”,這意味着父子進程間不共享這些存儲空間。
UNIX將複製父進程的地址空間內容給子進程,因此,子進程有了獨立的地址空間。在不同的UNIX (Like)系統下,我們無法確定fork之後是子進程先運行還是父進程先運行,這依賴於系統的實現。所以在移植代碼的時候我們不應該對此作出任何的假設。
為什麼fork會返回兩次?
由於在複製時複製了父進程堆棧段,所以兩個進程都停留在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函數中返回,箭頭表示各自的執行處。當父子進程有一個想要修改數據或者堆棧時,兩個進程真正分裂。
示例代碼:
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>

int main(int argc,char *argv[]){
    pid_t pid=fork();
    if ( pid < 0 ) {
        fprintf(stderr,"錯誤!");
    } else if( pid == 0 ) {
        printf("子進程空間");
        exit(0);
    } else {
        printf("父進程空間,子進程pid為%d",pid);
    }
    // 可以使用wait或waitpid函數等待子進程的結束並獲取結束狀態
    exit(0);
}
注意!樣例代碼僅供參考,樣例代碼存在着父進程在子進程結束前結束的可能性。必要的時候可以使用wait或 waitpid函數讓父進程等待子進程的結束並獲取子進程的返回狀態
fork()在Linux系統中的返回值是沒有NULL的.
Error Codes
出錯返回錯誤信息如下:
EAGAIN
達到進程數上限.
ENOMEM
沒有足夠空間給一個新進程分配.
fork函數的特點概括起來就是“調用一次,返回兩次”,在父進程中調用一次,在父進程和子進程中各返回一次。
fork的另一個特性是所有由父進程打開的描述符都被複制到子進程中。父、子進程中相同編號的文件描述符內核中指向同一個file結構體,也就是説,file結構體的引用計數要增加。 [2] 

fork例子

參考資料
  • 1.    Ahmad H A. Github開源軟件(OSS)項目中多種社交關係的挖掘與分析[D]. 哈爾濱工業大學, 2016.
  • 2.    Robert Love.Linux Kernel Development(Third Edition):機械工業出版社,2011.6