fork(複製行程)
/ fork /
想像一臺影印機,按一下就能複製出一位「做事到一半的員工」的近乎完美的分身,帶著相同的筆記、相同的待辦清單進度、相同開著的資料夾。現在有兩位員工,可以各自獨立地繼續做下去。在類 Unix 系統上,fork 就是藉由複製呼叫它的行程來建立新行程的系統呼叫:新的子行程是父行程的副本,此後兩者各自獨立地執行。
當行程呼叫 fork() 時,作業系統建立一個幾乎與父行程一模一樣的子行程:相同的程式碼、父行程記憶體(堆積、堆疊、資料)的一份副本、相同的開啟檔案,以及相同的執行位置。這一次呼叫會返回兩次,每個行程各一次:在父行程中它返回子行程的 PID(一個正數),在子行程中它返回 0,程式碼便靠這點分辨自己是哪一個行程並據此分支。為了不必立刻複製父行程的全部記憶體,真實系統採用寫入時複製:父行程與子行程一開始共用同一批被標為唯讀的實體頁,只有當其中之一寫入某頁時,那一頁才真正被複製。
fork 是 Unix 建立行程的經典方式,而且幾乎總是與 exec 搭配:子行程先 fork(做出父行程的副本),然後立刻呼叫 exec 把自己替換成另一個程式。這兩步(先 fork 再 exec)正是命令列直譯器啟動指令的方式。初學者常見的驚訝:fork 之後,父行程與子行程擁有各自獨立的記憶體,所以子行程改變的某個變數並不會在父行程中改變。它們起初完全相同,但如今已是兩個獨立的行程,不再共用彼此可寫的記憶體。
pid = fork(); if (pid == 0) { /* 子行程的程式碼 */ } else { /* 父行程的程式碼,pid 是子行程的 PID */ }。同一行程式碼在兩個行程裡執行,各走不同的分支。
一次呼叫,兩次返回:子行程得 0,父行程得子行程的 PID。
fork 之後,父行程與子行程擁有各自獨立的記憶體(底層採寫入時複製)。在其中一個改變變數不會改變另一個;它們是獨立的行程。