要搞清楚fork的執行過程,就必須先講清楚作業系統中的“進程(process)”概念。一個進程,主要包含三個元素: o. 一個可以執行的程式; o. 和該進程相關聯的全部資料(包括變數,記憶體空間,緩衝區等等); o. 程式的執行內容(execution context)。 不妨簡單理解為,一個進程表示的,就是一個可執行程式的一次執行過程中的一個狀態。作業系統對進程的管理,典型的情況,是通過進程表完成的。進程表中的每一個表項,記錄的是當前作業系統中一個進程的情況。對於單 CPU的情況而言,每一特定時刻只有一個進程佔用 CPU,但是系統中可能同時存在多個活動的(等待執行或繼續執行的)進程。 一個稱為“程式計數器(program counter, pc)”的寄存器,指出當前佔用 CPU的進程要執行的下一條指令的位置。 當分給某個進程的 CPU時間已經用完,作業系統將該進程相關的寄存器的值,儲存到該進程在進程表中對應的表項裡面;把將要接替這個進程佔用 CPU的那個進程的上下文,從進程表中讀出,並更新相應的寄存器(這個過程稱為“上下文交換(process context switch)”,實際的上下文交換需要涉及到更多的資料,那和fork無關,不再多說,主要要記住程式寄存器pc指出程式當前已經執行到哪裡,是進程內容相關的重要內容,換出 CPU的進程要儲存這個寄存器的值,換入CPU的進程,也要根據進程表中儲存的本進程執行內容資訊,更新這個寄存器)。 好了,有這些概念打底,可以說fork了。當你的程式執行到下面的語句: pid=fork(); 作業系統建立一個新的進程(子進程),並且在進程表中相應為它建立一個新的表項。新進程和原有進程的可執行程式是同一個程式;上下文和資料,絕大部分就是原進程(父進程)的拷貝,但它們是兩個相互獨立的進程!此時程式寄存器pc,在父、子進程的上下文中都聲稱,這個進程目前執行到fork調用即將返回(此時子進程不佔有CPU,子進程的pc不是真正儲存在寄存器中,而是作為進程上下文儲存在進程表中的對應表項內)。問題是怎麼返回,在父子進程中就分道揚鑣。 父進程繼續執行,作業系統對fork的實現,使這個調用在父進程中返回剛剛建立的子進程的pid(一個正整數),所以下面的if語句中pid<0, pid==0的兩個分支都不會執行。所以輸出i am the parent process... 子進程在之後的某個時候得到調度,它的上下文被換入,佔據 CPU,作業系統對fork的實現,使得子進程中fork調用返回0。所以在這個進程(注意這不是父進程了哦,雖然是同一個程式,但是這是同一個程式的另外一次執行,在作業系統中這次執行是由另外一個進程表示的,從執行的角度說和父進程相互獨立)中pid=0。這個進程繼續執行的過程中,if語句中pid<0不滿足,但是pid==0是true。所以輸出i am the child process... 我想你比較困惑的就是,為什麼看上去程式中互斥的兩個分支都被執行了。在一個程式的一次執行中,這當然是不可能的;但是你看到的兩行輸出是來自兩個進程,這兩個進程來自同一個程式的兩次執行。 另外:fork前父進程的東西子進程可以繼承,而在fork後子進程沒有任何和父進程的繼承關係了。在子進程裡建立的東西是子進程的,在父進程建立的東西是父進程的。可以完全看成兩個進程。 看一個例子: #include <unistd.h>; #include <sys/types.h>; main () { pid_t pid; printf("fork!"); // printf("fork!/n"); pid=fork(); if (pid < 0) printf("error in fork!"); else if (pid == 0) printf("i am the child process, my process id is %d/n",getpid()); else printf("i am the parent process, my process id is %d/n",getpid()); } 結果是 [root@localhost c]# ./a.out fork!i am the child process, my process id is 4286 fork!i am the parent process, my process id is 4285 但改成printf("fork!/n");後,結果是 [root@localhost c]# ./a.out fork! i am the child process, my process id is 4286 i am the parent process, my process id is 4285 為什麼只有一個fork!列印出來了?上一個為什麼有2個? APUE的P143說: fork之前調用了printf一次,當fork以後,該行資料仍在緩衝中,然後父進程資料空間複製到子進程中,該緩衝的資料也被複製到子進程中。 在printf()與fork()之間再加一個函數fflush(0),就可以看出是怎麼加事了。 #include <stdio.h> int main(void) { printf("hello"); fflush(stdout); fork(); exit(0); } 這樣只輸出hello 原因是:stdin,stdout,stderr都是行緩衝 清空緩衝就不會有兩個輸出了:setbuf(stdout, NULL)或setvbuf(stdout, NULL, _IONBF, 0) |