1. 程式人生 > >為什麼fork呼叫會返回兩次

為什麼fork呼叫會返回兩次

    fork大家可能都比較熟悉,呼叫一次返回2次,返回pid>0為父程序,pid=0為子程序。一直對fork方法如何返回兩次有些疑惑,函式呼叫一次通常只返回一個結果,fork呼叫怎麼會返回2次?之前查過原因,時間久了有些忘了,今天上網又查了下,其實大家說的“返回兩次”在表達上時不清楚的。 fork實際上並不是執行兩次,它依然返回了一次,準確地說是一次多一點,只是OS對fork進行的操作使得我們看起來它返回了兩次而已。
    要搞清楚fork的執行過程,就必須先講清楚作業系統中的“程序(process)”概念。一個程序,主要包含三個元素: 
    1) 一個可以執行的程式; 
    2)和該程序相關聯的全部資料(包括變數,記憶體空間,緩衝區等等); 
    3)程式的執行上下文(execution context)。 

    不妨簡單理解為,一個程序表示的,就是一個可執行程式的一次執行過程中的一個狀態。作業系統對程序的管理,典型的情況,是通過程序表完成的。程序表中的每一個表項,記錄的是當前作業系統中一個程序的情況。對於單 CPU的情況而言,每一特定時刻只有一個程序佔用 CPU,但是系統中可能同時存在多個活動的(等待執行或繼續執行的)程序。 

    一個稱為“程式計數器(program counter, pc)”的暫存器,指出當前佔用 CPU的程序要執行的下一條指令的位置。 

    當分給某個程序的 CPU時間已經用完,作業系統將該程序相關的暫存器的值,儲存到該程序在程序表中對應的表項裡面;把將要接替這個程序佔用 CPU的那個程序的上下文,從程序表中讀出,並更新相應的暫存器(這個過程稱為“上下文交換(process context switch)”,實際的上下文交換需要涉及到更多的資料,那和fork無關,不再多說,主要要記住程式暫存器pc指出程式當前已經執行到哪裡,是程序上下文的重要內容,換出 CPU的程序要儲存這個暫存器的值,換入CPU的程序,也要根據程序表中儲存的本程序執行上下文資訊,更新這個暫存器)。 

再來說說fork,為什麼會返回兩次?

    當程式執行到下面的語句: pid=fork();       由於在複製時複製了父程序的堆疊段,所以兩個程序都停留在fork函式中,等待返回。 因此fork函式會返回兩次,一次是在父程序中返回,另一次是在子程序中返回,這兩次的返回值是不一樣的。     fork呼叫的一個奇妙之處就是它僅僅被呼叫一次,卻能夠返回兩次,它可能有三種不同的返回值:
    1)在父程序中,fork返回新建立子程序的程序ID;        2)在子程序中,fork返回0;        3)如果出現錯誤,fork返回一個負值。         我們可以通過fork返回的值來判斷當前程序是子程序還是父程序。 引用一位網友的話來解釋fork函式返回的值為什麼在父子程序中不同。“
其實就相當於連結串列,程序形成了連結串列,父程序的fork函式返回的值指向子程序的程序id, 因為子程序沒有子程序,所以其fork函式返回的值為0 .
    呼叫fork之後,資料、堆、棧有兩份,程式碼仍然為一份但是這個程式碼段成為兩個程序的共享程式碼段都從fork函式中返回。當父子程序有一個想要修改資料或者堆疊時,兩個程序真正分裂。
    子程序程式碼是從fork處開始執行的, 為什麼不是從#include處開始複製程式碼的?這是因為fork是把
程序當前的情況拷貝一份 執行fork時,程序已經執行完了int count=0; 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 %\/n",getpid());  
}

結果是  
[[email protected] 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");後,結果是 
[[email protected] 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)


【參考部落格】 1. http://blog.csdn.net/barfoo/article/details/1626938 2. http://blog.csdn.net/happyguys12345/article/details/52550180 3. http://blog.csdn.net/dianacody/article/details/22401475