c 中的 forking、waitpid 问题

发布于 2024-11-05 16:03:52 字数 2443 浏览 6 评论 0原文

由于某种原因,这段代码立即执行父母命令,终止我的信号量并搞乱我对其他程序的流量控制。谁能告诉我为什么 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元素,包括管道。如果有更好的方法,我很想听听。

谢谢@乔纳森

For some reason this code executes the parental commands immediately, terminating my semaphores and screwing up my flow control of other programs. Can anyone tell me why the waitpid() isnt working?

    //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);



}

}

Thanks!

EDIT: Ok, I replaced waitpid with:

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

and that got me part of the way there. It isnt executing the parental controls immediately, but it seems to never execute now. As far as the pipe issue you talked about, program 1 (this one) is supposed to terminate all IPC elements, including the pipes. If there is a better way, I would love to hear it.

Thanks @Jonathan

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

怎言笑 2024-11-12 16:03:52

您只需等待一个进程完成,而不是等待所有进程完成。这可能是一个问题。使用 waitpid() 上的循环进行修复,直到返回“no more kids”。

代码的结构还有一些不足之处——它是一个嵌套 if 的兔子窝;恶心!

我担心在执行其他命令之前您没有关闭足够的管道。如果命令不依赖于检测管道上的 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() 的调用尽可能规则和对称。


克尼根和Plauger 的“编程风格的要素”(第 2 版,1978 年;很难找到,我怀疑)包含许多精彩的引言。直接相关的是(加粗强调,原文斜体):

  • [T]子例程调用允许我们总结参数列表中的不规则,这样我们就可以快速了解发生了什么。
  • 子例程本身总结了代码的规律,因此不需要使用重复的模式。

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

You only wait for one process to complete - not for all processes to complete. That is probably one problem. Fix with a loop on waitpid() until it returns 'no more kids'.

The structure of the code leaves something to be desired - it is a rabbit's warren of nested if's; ick!

I worry that you are not closing enough pipes before the other commands are executed. You may be OK if the commands do not depend on detecting EOF on a pipe; otherwise, you are in for a long wait.

You need a function like:

#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);
}

This simplifies your error handling. You can also do things like automatically add the PID that is dying, or the error that triggered the exit, if you wish.

We can also create a function to run another command:

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;
}

With those in place, we can look to reduce your code to this...

// 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 of these have the shmarg and some don't - is that inconsistency intentional or accidental? We'll assume intentional, so we need two versions of '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;
}

And then:

// 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);

If it was my code, the names of the variables would be more uniform - and probably in arrays:

// 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]);

Then, finally, you have the code:

// 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);

I am deeply suspicious that the run_cmdX() functions also need to close a large selection of the pipes - at least every descriptor of the pipes not intended for communication with their sub-process.

Organizing that cleanly is trickier - but can be done with care. I'd probably create the pipes in a single array:

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");

Then I'd create a function:

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

Then it can be called to close the unneeded pipes:

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

You just have to arrange for the right mask to be passed with each of the run_cmdN() functions, and the correct calls to be made. If the pipes array is not global, that will need to be passed too. I'd also look at how to encode the data tidily so that the calls to run_cmdN() are as regular and symmetric as possible.


Kernighan & Plauger's "The Elements of Programming Style" (2nd Edn, 1978; hard to find, I suspect) contains many magnificent quotes. The immediately apposite one is (bold emphasis added, italics in original):

  • [T]he subroutine call permits us to summarize the irregularities in the argument list where we can quickly see what is going on.
  • The subroutine itself summarizes the regularities of the code, so repeated patterns need not be used.

This can be viewed as part of the DRY (Don't Repeat Yourself) principle of programming. The err_exit() function call encapsulates three or four lines of code - a print and an exit plus the braces, depending on your preferred layout. The run_command() functions are a prime case of DRY. The proposed pipe_closer()is yet another.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文