2017-02-21 23 views
-1

我是一名初學者,正在努力學習如何fork()和wait()函數的工作。分叉/等待C程序。我應該輸出什麼?我的輸出是否正確?

有人可以運行我的代碼,並告訴我我的輸出應該是什麼?

現在我越來越: 一個 乙 Ç 一個 乙 Ç 一個 d Ë

然而,我的一個朋友說,這應該是: 一個 乙 Ç A D E A B C

而另一說,它應該是: 一個 乙 Ç Ç d Ë

因爲等待的()函數,我想子進程有父之前完成。這就是爲什麼我期望輸出以'E'結尾的原因。

然後會有什麼可能的輸出?我不明白我什麼時候運行它,我得到ABCABCADE。 'A'是否只能在初始子進程中打印一次?

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

int main(void) { 
int pid; 

    pid= fork(); 
    if (pid == 0) { 
     fprintf(stdout, "A\n"); 
     pid= fork(); 
     if (pid==0) { 
      fprintf(stdout, "B\n"); 
      pid=fork(); 
      fprintf(stdout, "C\n"); 
     } 
     else { 
      wait(NULL); 
      fprintf(stdout, "D\n"); 
     } 
    } 
    else { 
     fprintf(stdout, "E\n"); 
     wait(NULL); 
    } 
    // your code goes here 
    return(0); 
} 
+0

你寫了什麼「你的代碼放在這裏」?反正輸出不是確定的;你可以在不同的運行中得到不同的結果。另外,如果管道輸出或將輸出重定向到文件,則可以再次獲得不同的結果。 –

回答

0

它沒有規定父母或孩子是否第一次運行時你fork(),或者反正多久這個過程的運行或有多遠它得到其他接管之前,或兩者其實無論是在不同的內核同時運行。如果父母成功爲其子女wait(),那麼只要該wait()返回,確定子女已​​終止。然而,如果缺少其他同步手段,則不可能通過wait()相對於兒童執行的動作來預測父母在fork()和收集該孩子之間的動作順序。

還要注意涉及fork()調用返回值的條件。一個成功的fork()在子(僅)中返回0,所以這些將你的程序的大部分行爲都綁定到一個進程。

但是這裏還有另一個因素:多個句柄在同一個打開的文件描述中的交互。當你fork(),你最終有兩個stdout流(都指的是相同的開放文件描述),以前你有一個。關於程序如何處理這種情況,POSIX將some restrictions。如果您的程序的標準輸出是行緩衝的,這是連接到終端時的默認輸出,程序行爲由於要打印的每個字符串末尾的換行符而定義得很好。但是,如果stdout是完全緩衝的,那麼當它連接到管道時很可能需要fflush(stdout),然後才能分叉以確定行爲。因此,在分叉之前對fflush()是最安全的,確保程序行爲在不考慮執行環境的情況下被定義。

如果您根據這些考慮因素分析程序,並假定程序運行的方式完全可以給出定義的行爲,您將看到有多種可能的輸出,但您的建議不在其中。如果程序運行的方式使其行爲不明確,則對輸出沒有任何說法。

+0

然後會有什麼可能的輸出?我不明白我什麼時候運行它,我得到ABCABCADE。 'A'是否只能在初始子進程中打印一次? – SuperHippo

+0

@SuperHippo,這是一個很好的觀點,需要更新我的答案,你現在將會看到。最重要的是,你的程序不僅展現了un *指定的行爲,還展示了un * defined *行爲。 –

+0

@SuperHippo,已更新。 –

1

沒有理由認爲E應該最後出現,因爲你沒有wait()直到後打印E

有一個額外的複雜因素,您不一定要使用行緩衝輸出,並且如果在分叉之前有任何待定輸出,則父級和子級都會輸出緩衝文本。

讓我們在每個fork()之前加上fflush(stdout);。如果我們這樣做,我們將擺脫多個A輸出,我們可以推斷其餘的。這裏有一個時間線:

parent 
| 
| 
+------\ 
|  | 
"E" "A" 
|  | 
wait +------\ 
.  |  | 
.  wait "B" 
.  .  | 
.  .  +------\ 
.  .  |  | 
.  .  "C" "C" 
.  .  | 
.  |<----exit 
.  "D" 
.  | 
|<----exit 
| 

你可以看到,E可以在任何時候進行打印,但D不會被打印出來後至少一個C(左邊的一個)。

如果你換的

fprintf(stdout, "E\n"); 
    wait(NULL); 

的順序,你可以確保E總是後D(這又是至少一個C後),但其他C仍然可能是最後一次,因爲沒有與該進程的退出順序關係。

0

輸出不完全確定,因此您可能會在不同的運行中獲得不同的結果。另外,如果你管輸出,或重定向到一個文件,如果你不這樣做,你會得到一個不同的結果;詳情請參閱printf() anomaly after fork()

這是你的代碼的修改,可以強制刷新輸出(它使用POSIX標準<sys/wait.h>頭,而不是非標準<wait.h>頭,太。

#include <stdio.h> 
#include <unistd.h> 
#include <sys/wait.h> 

static int flush = 0; 

static void print(const char *str) 
{ 
    printf("%s\n", str); 
    if (flush) 
     fflush(stdout); 
} 

int main(int argc, char **argv) 
{ 
    if (argc > 1) 
     flush = (argv[argc] == 0); 
    int pid = fork(); 
    if (pid == 0) 
    { 
     print("A"); 
     pid = fork(); 
     if (pid == 0) 
     { 
      print("B"); 
      pid = fork(); 
      print("C"); 
     } 
     else 
     { 
      wait(NULL); 
      print("D"); 
     } 
    } 
    else 
    { 
     print("E"); 
     wait(NULL); 
    } 
    return(0); 
} 

最經常對我來說,當運行無(沒有重定向,沒有命令行參數)時,會出現E第一:

E 
A 
B 
C 
C 
D 

當管道輸送到cat,輸出變化,但是成爲在主題的變化:

A 
B 
C 
A 
D 
A 
B 
C 
E 

A 
B 
C 
A 
B 
C 
A 
D 
E 

當帶參數運行,以強制刷新發生,我得到這個始終如一:

E 
A 
B 
C 
D 
C 

調度允許運行在不同的過程序列;我的機器上的調度程序確實以不同的順序運行這些進程。父進程通常會一直運行,直到它遇到wait(),但孩子們沒有立即安排,所以它的輸出可以出現在任何孩子之前。

YMMV。

在Mac上測試(macOS Sierra 10.12.3,GCC 6.3.0)。

相關問題