2016-10-05 55 views
2

我正在運行以下代碼,這是我在Mac上用c寫的。撥打電話後打印出來的額外數據()

#include <unistd.h> 
#include <stdio.h> 

int main(int argc, const char * argv[]) { 

    int i = 0; 
    printf("Starting pid:%d\n", getpid()); 
    while(i++<3){ 
     int ret = fork(); 
     printf("hello(%d)%d, %d ", i, getpid(), ret); 
     if(!ret){ 
      printf("\n"); 
      return 0;} 
    } 
} 

我腦子裏想的目的是爲了有6幅版畫總 - 從原來的父3(PID「啓動PID」打印),並從每個3個催生孩子的1以上。相反,我收到的是:

Starting pid:7998 
hello(1)8009, 0 
hello(1)7998, 8009 hello(2)8010, 0 
hello(1)7998, 8009 hello(2)7998, 8010 hello(3)7998, 8011 hello(1)7998, 8009 hello(2)7998, 8010 hello(3)8011, 0 

而且,原父(7998)打印6次(三次當我== 1次,用我== 2,和曾經與我== 3)。

爲什麼會發生這種情況?爲什麼我沒有收到來自父母的三張照片(一次當我== 1,我== 2,我== 3)。如果我們從父母的角度來看它 - 有一個while循環,我們應該只打印3次。

叉可能是異步或什麼的?我錯過了什麼?

回答

7

當你在fork一個進程中,父進程中的內存被複制到子進程中。這包括stdout緩衝區。

從父級循環的第二次迭代開始,緩衝區中有hello(1)7998, 8009。當第二個孩子分叉時,這個文本仍然在那個孩子的緩衝區中。第二個孩子然後打印hello(2)8010, 0,然後換行。換行刷新孩子的緩衝器,所以第二子打印這樣的:

hello(1)7998, 8009 hello(2)8010, 0 

叉之後,其被添加到輸出緩衝器中的父打印hello(2)7998, 8010。然後在第三次迭代,第三個孩子繼承了這一點,並打印:

hello(1)7998, 8009 hello(2)7998, 8010 hello(3)8011, 0 

父然後打印hello(3)7998, 8011這再次加入緩衝液。然後父母然後退出循環和退出,在此之後,緩衝刷新和父輸出:

hello(1)7998, 8009 hello(2)7998, 8010 hello(3)7998, 8011 

爲了糾正這一點,那麼父進程需要分叉之前清除輸出緩衝區。

這可以通過調用printf後主叫fflush(stdout),或通過添加一個新行(\n)到printf的端部來實現。

編輯:

作爲一個更通用的解決方案,呼籲fflush(NULL)將刷新所有打開的輸出流。

+0

優秀。重要的是緩衝區內容的複製。 –

+0

哇..只是哇。 –

+1

'fflush(NULL)'刷新所有打開的輸出文件。 –

0

叉將過程分爲兩部分。在每個子進程中。你最終在3分叉之後有8個進程(除非你有條件地做)

+2

我立即殺死子進程 - 他們不分叉。 –

+0

嗯,這是一個有趣的事情,現在你提到... –

+0

而且我看到其中一個孩子產生了一個子女。 –

3

我認爲你所看到的只是流程本地內容 - 我將最終 - 寫入到 - 緩衝區正被複制到fork上,因此在fork之前排隊的內容將被多個進程單獨輸出。

分叉是做你的想法,措施的工具是有缺陷的。

我修改你的代碼中添加沖洗:

int ret = fork(); 
    printf("hello(%d)%d, %d ", i, getpid(), ret); 
    fflush(stdout); 

這保證了printf的結果,在過程中本地緩衝區不坐。它直接寫入stdout,然後本地緩衝區被清空。然後輸出變爲:

Starting pid:41731 
hello(1)41731, 41732 hello(2)41731, 41733 hello(1)41732, 0 
hello(3)41731, 41734 hello(2)41733, 0 
hello(3)41734, 0 

即預期的6次呼叫。 (我在Mac上使用CodeRunner來測試它;它也顯示了9個結果,沒有flush,所以我相信我重複了初始條件)。

+0

完美!謝謝!什麼是沖洗幹什麼? –

+0

@JohnBollinger在同一時間,使用flush可以解決這個問題,這很奇怪 –

+0

只要確保該進程正在寫入所有必須標準輸出的東西,而不是將其保存在內部緩存中。我會下注緩存,當你fork時,所有hello(1)7998實際上並不是來自進程7998,它們只是被進程7998入列,然後被排入隊列的文本被複制過來。由於'fflush'清除緩衝區,因此沒有任何可複製的內容。 (編輯:和答案修改,因爲這是一個相當令人信服的原因) – Tommy