首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++中的forking,waitpid问题

C++中的forking,waitpid问题
EN

Stack Overflow用户
提问于 2011-05-09 08:21:24
回答 1查看 3.8K关注 0票数 0

由于某些原因,这段代码会立即执行父命令,终止我的信号量,并搞乱我对其他程序的流控制。有人能告诉我为什么waitpid()不工作吗?

代码语言:javascript
复制
    //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);



}

}

谢谢!

编辑: Ok,我将waitpid替换为:

代码语言:javascript
复制
while (pid = waitpid(-1, NULL, 0)) {
       if (errno == ECHILD) {
          break;
       }
    }

这让我走了一段路。它不会立即执行家长控制,但现在似乎永远不会执行。就您谈到的管道问题而言,程序1(这个)应该终止所有IPC元素,包括管道。如果有更好的办法,我很想听听。

谢谢@Jonathan

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2011-05-09 08:30:15

您只需等待一个进程完成,而不是所有进程都完成。这可能是一个问题。修复waitpid()上的一个循环,直到它返回“不再有孩子”。

代码的结构不尽如人意--它是一堆嵌套的if;ick!

我担心您在执行其他命令之前没有关闭足够的管道。如果命令不依赖于检测管道上的EOF,那么您可能没有问题;否则,您将等待很长时间。

您需要一个类似以下的函数:

代码语言:javascript
复制
#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,或者触发退出的错误。

我们还可以创建一个函数来运行另一个命令:

代码语言:javascript
复制
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;
}

有了这些,我们就可以把你的代码简化成这样……

代码语言:javascript
复制
// 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);

其中的Hmmm...some有shmarg,而有些没有--这是故意的还是偶然的?我们假设是故意的,所以我们需要‘run_command()’的两个版本:

代码语言:javascript
复制
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;
}

然后:

代码语言:javascript
复制
// 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);

如果这是我的代码,变量的名称会更统一--而且可能是在数组中:

代码语言:javascript
复制
// 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]);

然后,最后,您就有了代码:

代码语言:javascript
复制
// 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()函数还需要关闭大量的管道-至少是不打算与其子进程通信的管道的每个描述符。

干净利落地组织起来比较棘手--但要小心才能做到。我可能会在单个数组中创建管道:

代码语言:javascript
复制
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");

然后我会创建一个函数:

代码语言:javascript
复制
void pipe_closer(int *pipes, int close_mask)
{
    for (i = 0; i < 8; i++)
    {
         if ((mask & (1 << i)) != 0)
             close(pipes[i]);
    }
}

然后可以调用它来关闭不需要的管道:

代码语言:javascript
复制
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“(第二版,1978年;我怀疑很难找到)包含了许多华丽的语录。直接贴切的是(加粗强调,原文为斜体):

子例程调用允许我们在参数列表中汇总代码irregularities ,其中我们可以快速了解发生了什么。子例程本身汇总了代码的,因此不需要使用重复的模式。

这可以看作是编程的DRY (不要重复自己)原则的一部分。err_exit()函数调用封装了三到四行代码-一个打印和一个出口加上大括号,具体取决于您的首选布局。run_command()函数是DRY的一个典型例子。提议的pipe_closer()是另一个。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5931309

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档