2016-08-03 32 views
1

程序在從scanf取4或5個值後終止。 但我希望它接受8個值(總共8個進程),然後終止。如何等待每個進程在以下示例中終止?

void main() { 
    fork(); 
    fork(); 
    fork(); 

    scanf("%d",&j); 
    printf("The values are %d\n",j); 

    wait(0); 
} 
+2

你是如何運行的程序,是什麼讓你認爲只有四五值被接受?請注意,如果stdin被重定向到管道,第一個調用scanf的進程可能會排空管道,其餘進程將看到EOF。 – rici

+0

@rici,不太可能。每個進程在文件描述符上執行一個'scanf(3)'(其中附加條件是該文件是'dup(2)'並且讀取是順序的)。這裏的問題是當進程組頭部死亡時將其置於讀隊列中),剩下的進程被內核殺死。 –

+0

經過測試,內核不會終止進程。但是,一旦組長領導去世,他們就會從輸入描述符中獲得'EOF'。對不起,但通過測試我無法編輯我最後的評論。 –

回答

2

那麼,你必須回答自己的第一個問題是你多少進程覺得你有嗎?由於您沒有使用fork(2)系統調用中的返回值,因此即使您是父項或者在每個分支執行後您都是子項,您也不知道。

您將在故事的開頭,它做了fork(2)系統調用和2個過程轉換有一個過程,即再執行(包括,家長和孩子)第二fork(2)電話,轉換(每個)在第二次調用返回時,在兩個進程中(總共多兩個,總共四個)。然後是第三個,它再次複製了進程的數量,因此您將在完全填充到第三個高度的二叉樹執行歷史層次結構中,在故事的末尾八個運行進程。

但過程的關係是不一樣的

該方法可以每個wait(2)爲他們的每一個自己孩子,但有一些,因爲他們的inceptions取得過程三,二,一,或者根本沒有fork()。根節點是唯一使三個fork() s,所以它可以做三個wait(2)沒有錯誤(對於它的每個孩子),其第一個孩子,只有兩個,第二個只有一個......像這樣:

proc[0]---fork()-----fork()---fork()----exit(); 
      |   |  | 
      |   |  proc[3]---exit(); 
      |   |  
      |   proc[2]--fork()----exit(); 
      |     | 
      |     proc[4]---exit(); 
      | 
      proc[1]----fork()---fork()----exit(); 
        |  | 
        |  proc[5]---exit(); 
        | 
        proc[6]--fork()----exit(); 
           | 
           proc[7]---exit(); 

所以

  • proc[0]可以wait(2)proc[1]proc[2]proc[3];
  • proc[1] can wait(2) to proc[5] and proc[6];
  • proc[2]可以wait(2)proc[4]
  • proc[3]不能wait(2)(它會導致錯誤,如果wait(2)被稱爲);
  • proc[4]不能wait(2);
  • proc[5]不能wait(2);
  • proc[6]可以wait(2)proc[7]
  • proc[7]不能wait(2)

由於wait(2)只能等待這樣的一個孩子(孩子必須用叉子來創建,或者調用將導致錯誤),你必須發出許多wait(2)電話爲fork(2)是你已發出,以等待他們,所以你必須控制你有的孩子的數量(如你所見,這個數字是不同的每個進程)。你可以,例如,增加一個計數器(從fork(2)收到0結果的過程中,讓你知道你已經發布到現在。

int main() 
{ 
    int forks_made = 0; 
    if (fork() > 0) forks_made++; 
    if (fork() > 0) forks_made++; 
    if (fork() > 0) forks_made++; 
    for (i = 0; i < forks_made; i++) wait(NULL); 
    exit(0); 
} 

或者乾脆fork(2) S上的號碼,你可以wait(2)直到一個錯誤的系統調用的結果(你沒有更多的孩子)

int main() 
{ 
    fork(); 
    fork(); 
    fork(); 
    while(wait(NULL) == 0) 
     continue; 
    exit(0); 
} 

要小心,因爲工藝層次是從二進制歷史樹不同的樹。公關ocess層次是這樣的:

proc[0] 
| 
+--proc[1] 
| | 
| +--proc[5] 
| | 
| `--proc[6] 
|  | 
|  `--proc[7] 
| 
+--proc[2] 
| | 
| `--proc[4] 
| 
`--proc[3] 

假設我寫了下面的代碼:

int main() 
{ 
    fork(); fork(); fork(); 
    wait(0); wait(0); wait(0); 
    exit(0); 
} 

結果是:

       ok    ok   ok 
p[0]-f()-f()-f()----------------w()------------w()--------w()-exit(); 
    | | |    ^   ^  ^
    | | |  err err err |    |   | 
    | | +-p[3]-w()-w()-w()-exit();  |   | 
    | |          |   | 
    | |       ok err err |   | 
    | +-p[2]-f()----------------w()-w()-w()-exit(); | 
    |   |    ^     | 
    |   |     |      | 
    |   |  err err err |      | 
    |   +-p[4]-w()-w()-w()-exit();    | 
    |        ok    ok err | 
    +-p[1]-f()-f()----------------w()------------w()-w()-exit(); 
      | |    ^   ^
      | |  err err err |    | 
      | +-p[5]-w()-w()-w()-exit();  | 
      |       ok err err | 
      +-p[6]-f()----------------w()-w()-w()-exit(); 
        |    ^
        |  err err err | 
        +-p[7]-w()-w()-w()-exit(); 

注:

即使兒童死於父母做了wait(2)該ker nel將它們保留在進程表中(但沒有分配任何資源),因爲殭屍進程只是等待父進程正確地執行wait(2)系統調用。這就是爲什麼內核知道你可以執行wait(2)(或者你只能等到fork(2))。

注2:

爲什麼只處理read(2) S中的結果和完成的一部分嗎?嗯,我一直在閱讀一些文件,並做一些測試,並在行爲上我已經測試了三種操作系統不同:

  • MacOS X的我已經測試過,並在過程組長exit(2) S,所有的孩子從read(2)呼叫喚醒,並給出ETIMEDOUT錯誤(令人驚訝的)
  • FreeBSD。在FreeBSD上,結果是相似的,但有不同的錯誤(EIO)。
  • Linux。在Linux上,終端驅動程序爲每個進程在原始模式下只提供一個輸入字符(即使有更多的字符)(它們在讀取時輸出每個字符)這是迄今爲止最奇怪的行爲。

作爲正常作業控制使得殼重新獲取的控制端子和該進程組不再是終端裝置的控制組,終端應當喚醒所有的過程從它試圖read(2)錯誤代碼,所以也許FreeBSD是我們應該擁有的最一致的結果。

用來測試這種情況下,代碼如下:

#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
#include <signal.h> 
#include <unistd.h> 
#include <errno.h> 
#include <sys/types.h> 
#include <sys/wait.h> 

void hndlr(int sig) 
{ 
    printf("%d(prnt=%d,pgrp=%d): signal %d received\n", 
      getpid(), getppid(), getpgrp(), sig); 
} 

int main() 
{ 
    int i; 
    char line[1024]; 

    for (i = 1; i < 64; i++) 
     signal(i, hndlr); 
    printf("pid=%d, pgrp=%d\n", getpid(), getpgrp()); 
    fork(); 
    fork(); 
    fork(); 
    i = read(0, line, sizeof line); 
    switch(i) { 
    case -1: 
     printf("pid=%d, prnt=%d, pgrp=%d: read: %s(errno=%d)\n", 
       getpid(), getppid(), getpgrp(), strerror(errno), errno); 
     break; 
    case 0: 
     printf("pid=%d, prnt=%d, pgrp=%d: read: EOF\n", 
       getpid(), getppid(), getpgrp()); 
     break; 
    default: 
     printf("pid=%d, prnt=%d, pgrp=%d: read: [%*.*s]\n", 
       getpid(), getppid(), getpgrp(), i, i, line); 
     break; 
    } 
#if 0 
    wait(NULL); 
    wait(NULL); 
    wait(NULL); 
#endif 
} /* main */ 
1

這裏的問題是,wait(0)只等待第一個孩子。主進程一旦成功參加了自己的輸入就會退出,而直接的孩子也一樣。

最簡單的組裝機是隻是等待,直到有沒有更多的孩子等待:

void main() { 
    int j; 
    fork(); 
    fork(); 
    fork(); 

    scanf("%d", &j); 
    printf("The values are %d\n",j); 

    while (wait(0) != -1); 
} 

這種由交互終端讀取8個值符合市場預期。

雖然你不應該在真正的程序中這樣做。主線程應該讀取所​​有8個值並根據需要創建子進程來處理它們。不這樣做會導致競爭條件和緩衝問題。

這裏是這樣的一個例子,使用爲了簡潔少一個fork()

$ for i in {1..4}; do echo "$i"; done | ./foo 
The values are 1 
The values are 0 
The values are 0 
The values are 0 

$ for i in {1..4}; do echo "$i"; sleep 0.1; done | ./foo 
The values are 1 
The values are 2 
The values are 3 
The values are 4 

在前者的情況下,多次寫入到管成爲一個單一的讀出,它是由任意的處理拾取。其他人什麼也得不到。

在第二種情況下,添加短暫的睡眠以允許每次讀取之間有更多的時間,現在他們按照預期得到值。

+0

感謝您的回覆。我的要求是我應該監控所有8個Scanf的。無論哪種情況出現阻塞狀態都應該處理它們。隨着j的多個副本的製作,我不確定競爭情況如何。如果你以不同的方式做,請詳細說明你的方法。 – Harsha

+0

我添加了一個例子。更好的方法是:for(int i = 0; i <8; i ++){scanf(「%d」,&j); if(!fork())process(j);}',這樣只有一個進程從標準輸入讀取一次。 –

0

您需要等待所有的孩子和您的孩子需要等待他們的孩子等目前您的wait(0)電話只等待一個孩子改變狀態。

// wait(0); 
while (1) { 
    int status; 
    pid_t done = wait(&status); 
    if (done == -1) 
     break; // no more children 
} 
相關問題