Fork (系統呼叫)

在計算機領域中，尤其是Unix及類Unix系統作業系統中，fork是一種建立自身行程副本的操作。它通常是核心實現的一種系統呼叫。Fork是類Unix作業系統上建立程序的一種主要方法，甚至歷史上是唯一方法。概述.在多工作業系統中，行程需要一種方法來建立新程序，例如執行其他程式。Fork及其變種在類Unix系統中通常是這樣做的唯一方式。如果程序需要啟動另一個程式的可執行檔案，它需要先Fork來建立一個自身的副本。然後由該副本即“子程序”呼叫系統呼叫，用其他程式覆蓋自身：停止執行自己之前的程式並執行其他程式。Fork操作會為子程序建立一個單獨的定址空間。子程序擁有父程序所有記憶體段的精確副本。在現代的UNIX變種中，這遵循出自SunOS-4.0的虛擬記憶體模型，根據寫入時複製語義，物理記憶體不需要被實際複製。取而代之的是，兩個程序的可能指向物理記憶體中的同一個頁，直到它們寫入該頁時，寫入才會發生。在用fork配合exec來執行新程式的情況下，此最佳化很重要。通常來說，子程序在停止程式執行前會執行一小組有利於其他程式的操作，它可能用到少量的其父程序的資料結構。當一個程序呼叫fork時，它被認為是父程序，新建立的程序是它的孩子。在fork之後，兩個程序還執行著相同的程式，都像是呼叫了該系統呼叫一般恢復執行。然後它們可以檢查呼叫的確定其狀態：是父程序還是子程序，以及據此行事。fork系統呼叫在第一個版本的Unix就已存在，它借用於更早的 分時系統。Fork是標準化的POSIX的一部分。通訊.子程序從父程序的檔案描述符副本開始。對於程序間通訊，父程序通常會建立一個或多個管道，在fork程序之後，程序關閉它們不需要的管道端。變種.Vfork.Vfork是與fork具有相同呼叫約定和很多相同語義的一個變種，但只能在有限的情況下使用它。它起源於Unix的3BSD版本，這是首個支援虛擬記憶體的Unix版本。它已按POSIX標準化，這使得vfork能具有與fork完全相同的行為。但這已在2004年的版本中被標為過時，並在後續版本中被posix_spawn()取代。在發出一個vfork系統呼叫時，父程序被暫停，直至子程序完成執行或被新的可執行映像取代的函式，其他任何目的會導致未定義行為。使用vfork時，子程序借用而非複製資料結構，所以vfork仍比使用寫時複製語義的fork更快。System V在System VR4被引入前不支援此係統函式，因為它的記憶體共享容易出錯："Vfork" does not copy page tables so it is faster than the System V "fork" implementation. But the child process executes in the same physical address space as the parent process (until an "exec" or "exit") and can thus overwrite the parent's data and stack. A dangerous situation could arise if a programmer uses "vfork" incorrectly, so the onus for calling "vfork" lies with the programmer. The difference between the System V approach and the BSD approach is philosophical: Should the kernel hide idiosyncrasies of its implementation from users, or should it allow sophisticated users the opportunity to take advantage of the implementation to do a logical function more efficiently?——同樣，Linux對vfork的手冊頁面強烈不鼓勵它的使用：It is rather unfortunate that Linux revived this specter from the past. The BSD man page states: "This system call will be eliminated when proper system sharing mechanisms are implemented. Users should not depend on the memory sharing semantics of vfork() as it will, in that case, be made synonymous to fork(2)."使用vfork的其他問題包括死鎖 ，它可能發生在多執行緒程式中，由於與動態連結互動。 作為vfork介面的替代品，POSIX引入了posix_spawn函式家族，它結合了fork和exec的動作。這些函式可以實現為fork的程式庫例程，就像Linux那樣，或者為了更好的效能實現為vfork ，就像Solaris那樣。不過，POSIX規範中註明它是“為核心操作設計”，尤其是用於執行在受限硬體和實時系統上的作業系統。雖然4.4BSD的實現中擺脫了vfork的實現，使vfork做到與fork相同的行為，它在NetBSD作業系統中因效能原因而恢復。一些嵌入式作業系統而不可能實現寫時複製的裝置上操作。Rfork.Plan 9作業系統由Unix的設計者創造，包括fork，但也有一個名為“rfork”的變種，它允許父程序與子程序之間資源的細粒度共享，包括地址空間、環境變數和檔案系統名稱空間；這使它成為了建立程序和其中的執行緒的一個統一介面。 在FreeBSD和IRIX中採用了來自Plan 9的rfork，後者將其更名為“sproc”。Clone.“clone”。其他作業系統中的Fork.在VMS作業系統。VM/CMS應用程式範例.下列Hello World程式的變種以C語言展示了fork系統呼叫的機理。該程式#重定向 為兩個程序，每個都基於#重定向 系統呼叫的返回值決定它們執行什麼功能。中的標頭檔案等已被省略。int main(void)pid_t pid = fork();if (pid == -1) {perror("fork failed");exit(EXIT_FAILURE);else if (pid == 0) {printf("Hello from the child process!\n");_exit(EXIT_SUCCESS);else {int status;(void)waitpid(pid, &amp;status, 0);return EXIT_SUCCESS;下面是該程式的解析：pid_t pid = fork();呼叫中的第一句是呼叫#重定向 系統呼叫來分割執行為兩個程序。#重定向 的返回值被記錄在型別為pid_t的變數中，其中是POSIX型別的程序識別符號。在計算機領域，尤其是Unix及類Unix系統作業系統中，fork是一種建立自身行程副本的操作。它通常是核心實現的一種系統呼叫。Fork是在類Unix作業系統上建立程序的一種主要方法，甚至歷史上曾是唯一方法。-1錯誤表示#重定向 出錯：沒有新程序被建立。因此要印出一條錯誤訊息。如果#重定向 成功，那麼現在有兩個程序。兩者都從#重定向 返回時開始執行#重定向 函式。為了使程序執行不同的任務，程式必須基於#重定向 的返回值決定其作為子程序或父程序執行某個分支。else if (pid == 0) {printf("Hello from the child process!\n");_exit(EXIT_SUCCESS);Fork操作會為子程序建立一個單獨的定址空間。子程序擁有父程序所有記憶體段的精確副本。在現代的UNIX變種中，這遵循出自SunOS-4.0的虛擬記憶體模型，根據寫入時複製語義，物理記憶體不需要被實際複製。取而代之的是，兩個程序的可能指向物理記憶體中的同一個頁，直至它們寫入該頁時，寫入才會發生。在用#重定向 配合#重定向 來執行新程式的情況下，此最佳化很重要。通常，子程序在停止程式執行前會執行一小組有利於其他程式的操作，它可能用到少量的其父程序的資料結構。else {int status;(void)waitpid(pid, &amp;status, 0);其他程序——即父程序，會收到#重定向 傳來的子程序的程序識別符號，其始終為一個正數。父程序將此識別符號傳遞給 waitpid 系統呼叫來暫停執行，直至子程序退出。當此情況發生後，父程序繼續執行並按return語句的含義退出。