簡體   English   中英

分叉,c中的waitpid問題

[英]forking, waitpid problems in 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元素,包括管道。 如果有更好的方法,我很想聽聽。

謝謝@喬納森

您僅等待一個過程完成,而不必等待所有過程完成。 那可能是一個問題。 waitpid()上循環修復,直到返回“沒有更多的孩子”。

代碼的結構還有一些不足之處-它是嵌套if的兔子。 ick!

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

您需要一個類似的功能:

#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)的“ 編程風格要素 ”(第二版,1978年,我很難找到,我懷疑)包含許多引人注目的引號。 緊隨其后的是(粗體字加粗斜體)。

  • 子例程調用使我們可以總結參數列表不規則之處 ,從而可以快速了解發生了什么情況。
  • 子例程本身總結了代碼的規則性 ,因此無需使用重復的模式。

可以將其視為DRY(請勿重復自己)編程原理的一部分。 err_exit()函數調用封裝三到四行代碼-打印,出口和花括號,具體取決於您的首選布局。 run_command()函數是DRY的主要情況。 建議的pipe_closer()是另一個。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM