2011-05-09 22 views
0

出於某種原因,此代碼立即執行父母命令,終止我的信號量,並搞砸了我的其他程序的流量控制。任何人都可以告訴我爲什麼waitpid()不工作?分叉,c中的waitpid問題

//Create child processes 
pid = fork(); 
if(pid < 0){ 
    fprintf(stderr, "Fork Failed.\n"); 
    exit(1); 
    return; 
}else if(pid==0){ 
     if(execl("/home/tropix/hw11-2","/home/tropix/hw11-2",semarg,pipe_to_p3,pipe_to_p4,(char*)0)){ 
      fprintf(stderr, "File Exexecution of hw11-2 failed.\n"); 
      exit(1); 
     } 
} else { 
    pid = fork(); 
    if(pid < 0){ 
     fprintf(stderr, "Fork Failed.\n"); 
     exit(1); 
     return; 
    } else if(pid==0){ 
     if(execl("/home/tropix/hw11-3","/home/tropix/hw11-3",shmarg,semarg,pipe_from_p2,pipe_to_p5_1, (char*)0)){ 
      fprintf(stderr, "File Execution of hw11-3 failed.\n"); 
      exit(1); 
     } 
    } else { 
     pid = fork(); 
     if(pid < 0){ 
      fprintf(stderr, "Fork Failed.\n"); 
      exit(1); 
      return; 
     } else if (pid == 0){ 
      if(execl("/home/tropix/hw11-4","/home/tropix/hw11-4",shmarg,semarg,pipe_from_p2_2,pipe_to_p5_2, (char*)0)){ 
       fprintf(stderr, "File Execution of hw11-4 failed.\n"); 
       exit(1); 
      } 
     } else { 
      pid = fork(); 
      if(pid < 0){ 
       fprintf(stderr, "Fork Failed.\n"); 
       exit(1); 
       return; 
      } else if (pid == 0){ 
       if(execl("/home/tropix/hw11-5","/home/tropix/hw11-5",semarg,pipe_from_p3,pipe_from_p4,(char*)0)){ 
        fprintf(stderr, "File Execution of hw11-5 failed.\n"); 
        exit(1); 
       } 
      } else if (pid > 0) { 
      } 
     } 

    } 

    //Closing Pipes 
    close(pipe1[1]); 
    close(pipe2[1]); 
    close(pipe3[1]); 
    close(pipe4[1]); 
    close(pipe1[0]); 
    close(pipe2[0]); 
    close(pipe3[0]); 
    close(pipe4[0]); 

    //Wait for child process completetion 
    waitpid(pid,NULL,0); 
    printf("Child Processes Complete.\n"); 

    //Terminate Semaphores 
    semctl(sem_id,0,IPC_RMID); 

    //Terminate Shared Memory Segement 
    shmctl(shmid, IPC_RMID, NULL); 



} 

}

謝謝!

編輯:好吧,我換成waitpid函數有:

while (pid = waitpid(-1, NULL, 0)) { 
     if (errno == ECHILD) { 
      break; 
     } 
    } 

而且讓我的存在方式的一部分。它沒有立即執行家長控制,但它現在似乎永遠不會執行。就你提到的管道問題而言,程序1(這一個)應該終止所有的IPC元素,包括管道。如果有更好的方法,我很樂意聽到它。

謝謝@Jonathan

回答

6

您只等待一個進程完成 - 而不是所有進程完成。這可能是一個問題。修復waitpid()的循環,直到它返回「沒有更多的孩子」。

代碼的結構留下了一些需要的東西 - 它是一個嵌套if的兔子warren;麻煩!

我擔心在其他命令執行之前沒有關閉足夠的管道。如果這些命令不依賴於檢測管道上的EOF,則可能會OK;否則,你需要等待很長時間。

你需要像一個函數:

#include <stdarg.h> 

static void err_exit(const char *format, ...) 
{ 
    va_list args; 
    va_start(args, format); 
    vfprintf(stderr, format, args); 
    va_end(args); 
    exit(EXIT_FAILURE); 
} 

這簡化了錯誤處理。如果您願意,您還可以執行諸如自動添加正在死亡的PID或觸發退出的錯誤等。

我們也可以創建一個函數來運行另一個命令:在地方

static pid_t run_command(const char *cmd, const char *shmarg, const char *semarg, 
         const char *fdarg1, const char *fdarg2) 
{ 
    pid_t pid = fork(); 
    if (pid < 0) 
     err_exit("Failed to fork\n"); 
    else if (pid == 0) 
    { 
     execl(cmd, cmd, shmarg, semarg, fdarg1, fdarg2, (char *)0); 
     err_exit("Failed to exec %s\n", cmd); 
    } 
    return pid; 
} 

有了這些,我們可以看看,以減少你的代碼到這個...

// Create child processes 
pid_t pid1 = run_command("/home/tropix/hw11-2", semarg, pipe_to_p3, pipe_to_p4); 
pid_t pid2 = run_command("/home/tropix/hw11-3", shmarg, semarg, pipe_from_p2, pipe_to_p5_1); 
pid_t pid3 = run_command("/home/tropix/hw11-4", shmarg, semarg, pipe_from_p2_2, pipe_to_p5_2); 
pid_t pid4 = run_command("/home/tropix/hw11-5", semarg, pipe_from_p3, pipe_from_p4); 

嗯...其中一些有shmarg,有些則沒有 - 是不是有意或意外的不一致?我們假設故意的,所以我們需要「)run_command(」的兩個版本:

static pid_t run_cmd4(const char *cmd, const char *shmarg, const char *semarg, 
         const char *fdarg1, const char *fdarg2) 
{ 
    pid_t pid = fork(); 
    if (pid < 0) 
     err_exit("Failed to fork\n"); 
    else if (pid == 0) 
    { 
     execl(cmd, cmd, shmarg, semarg, fdarg1, fdarg2, (char *)0); 
     err_exit("Failed to exec %s\n", cmd); 
    } 
    return pid; 
} 

static pid_t run_cmd3(const char *cmd, const char *semarg, 
         const char *fdarg1, const char *fdarg2) 
{ 
    pid_t pid = fork(); 
    if (pid < 0) 
     err_exit("Failed to fork\n"); 
    else if (pid == 0) 
    { 
     execl(cmd, cmd, semarg, fdarg1, fdarg2, (char *)0); 
     err_exit("Failed to exec %s\n", cmd); 
    } 
    return pid; 
} 

然後:

// Create child processes 
pid_t pid1 = run_cmd3("/home/tropix/hw11-2", semarg, pipe_to_p3, pipe_to_p4); 
pid_t pid2 = run_cmd4("/home/tropix/hw11-3", shmarg, semarg, pipe_from_p2, pipe_to_p5_1); 
pid_t pid3 = run_cmd4("/home/tropix/hw11-4", shmarg, semarg, pipe_from_p2_2, pipe_to_p5_2); 
pid_t pid4 = run_cmd3("/home/tropix/hw11-5", semarg, pipe_from_p3, pipe_from_p4); 

如果這是我的代碼,該變量的名稱會更均勻 - 並可能在陣列:

// Create child processes 
pid_t pid1 = run_cmd3("/home/tropix/hw11-2",   semarg, pipearg[0], pipearg[1]); 
pid_t pid2 = run_cmd4("/home/tropix/hw11-3", shmarg, semarg, pipearg[2], pipearg[3]); 
pid_t pid3 = run_cmd4("/home/tropix/hw11-4", shmarg, semarg, pipearg[4], pipearg[5]); 
pid_t pid4 = run_cmd3("/home/tropix/hw11-5",   semarg, pipearg[6], pipearg[7]); 

然後,最後,你的代碼:

// Closing Pipes 
close(pipe1[1]); 
close(pipe2[1]); 
close(pipe3[1]); 
close(pipe4[1]); 
close(pipe1[0]); 
close(pipe2[0]); 
close(pipe3[0]); 
close(pipe4[0]); 

//Wait for child process completion 
while (waitpid(0, NULL, 0) != 0) 
    ; 

printf("Child Processes Complete.\n"); 

// Remove Semaphores and Shared Memory 
semctl(sem_id,0,IPC_RMID); 
shmctl(shmid, IPC_RMID, NULL); 

我非常懷疑run_cmdX()函數也需要關閉大量的管道選擇 - 至少每個管道的描述符都不用於與子進程通信。

乾淨地組織起來會更棘手 - 但可以小心謹慎地完成。我可能會在單個陣列中創建的管道:

if (pipe(&pipes[0]) != 0 || pipe(&pipes[2]) != 0 || 
    pipe(&pipes[4]) != 0 || pipe(&pipes[6]) != 0) 
    err_exit("Failed to create a pipe\n"); 

然後我會創造一個功能:

void pipe_closer(int *pipes, int close_mask) 
{ 
    for (i = 0; i < 8; i++) 
    { 
     if ((mask & (1 << i)) != 0) 
      close(pipes[i]); 
    } 
} 

然後,它可以調用關閉不需要的管道:

pipe_closer(pipes, 0xFF); // Close them all - main() 
pipe_closer(pipes, 0xFC); // All except 0, 1 
pipe_closer(pipes, 0xF3); // All except 2, 3 
pipe_closer(pipes, 0xCF); // All except 4, 5 
pipe_closer(pipes, 0x3F); // All except 6, 7 

您只需要安排正確的掩碼,以便與每個run_cmdN()函數一起傳遞,並調用正確的電話號碼。如果pipes數組不是全局的,那也需要傳遞。我還會看看如何整理數據,以便run_cmdN()的調用盡可能規則和對稱。


Kernighan的& Plauger的 「The Elements of Programming Style」(第2版,1978;難找,我懷疑)包含了許多宏偉的報價。緊接貼切的一項是(加粗加着重號,原斜體):

  • [T]他子程序調用允許我們總結違規參數列表,我們可以很快看到什麼繼續。
  • 該子程序本身總結了代碼規律性,所以不需要使用重複模式。

這可以看作是DRY(不要重複自己)編程原理的一部分。函數調用封裝了三行或四行代碼 - 打印和退出以及大括號,具體取決於您的首選佈局。 run_command()函數是DRY的一個主要情況。擬議的pipe_closer()是另一個。

+0

我收到過的最佳答覆。您,先生,是一名專業人士,我感謝您的時間! – Josh 2011-05-09 00:57:05