在程序完成之后,执行分支后的Exec不会返回结果

时间:2015-02-05 19:56:16

标签: c++ c pipe exec fork

我试图创建一个分叉的程序,当父级等待子进程终止时,这个子进程再次分叉,然后执行两个执行程序。程序上有一个Pipe,我已经检查了程序中每个dup2()和pipe()的返回值 - 这里省略了它们,使它看起来更简洁。问题是我只得到ls -a |的结果在程序完成后排序-r。 代码是:

#include <cstdio>
#include <cstring>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>

int main(int argc, char *argv[]) {
printf("Shell> \n"); fflush(stdout);

pid_t pid1;
pid_t pid2;
int status = 0;
int fd[2];

if(pipe(fd) < 0) {
    printf("FATAL ERROR.\n");
}   

pid1 = fork();

if(pid1 > 0) {      // Parent
    waitpid(pid1, &status, 0);
    printf("\t\t------PID1 Complete-------\n\n");                   
}
else {              // Child
    if(pid1 == 0) {
        printf("ON CHILD\n");

        pid2 = fork();

        if(pid2 > 0) {  // Child -> Parent
            printf("ON CHILD-Parent\n");
            close(fd[1]);
            dup2(fd[0], STDIN_FILENO);
            waitpid(pid2, &status, 0);
            printf("ON CHILD-Parent after wait\n");
            execlp("sort", "sort", "-r", NULL);
            perror("Problem with execlp\n");
            exit(1);
        }
        else {          // Child -> Child
            printf("ON CHILD->Child\n");
            close(fd[0]);
            dup2(fd[1], STDOUT_FILENO);
            execlp("ls", "ls", "-a", NULL);
            perror("Problem with execvp\n");
            exit(1);
        }               
    }   // End of if(pid1 == 0)
}   // End of Child

printf("\nEnd of program.\n");

return 0;
}

我目前的输出是:

Shell> 
ON CHILD
ON CHILD-Parent
ON CHILD->Child
ON CHILD-Parent after wait

我认为问题在于等待,但我无法弄清楚如何使这项工作。有任何想法吗?谢谢!

2 个答案:

答案 0 :(得分:3)

问题是您在祖父母进程中调用pipe。在孙子进程(ls -a)退出之后,父进程(sort -r)无限期地阻塞等待从管道读取更多输入,因为某个进程 - 祖父进程 - 将一个打开的描述符保存到管道的写端。

如果在祖父进程中关闭管道描述符,或者更好的是将pipe调用移动到第一个分叉进程中,那么排序进程将在最后一个具有开放描述符的进程结束时终止。管道退出(DEMO):

int main() {
    // Turn off buffering of stdout, to help with debugging
    setvbuf(stdout, NULL, _IONBF, 0);
    printf("Shell> \n");

    pid_t pid1 = fork();
    if(pid1 < 0) {
        perror("fork failed");
    }

    if(pid1 > 0) {      // Parent
        int status;
        waitpid(pid1, &status, 0);
        printf("\t\t------PID1 Complete (%d) -------\n\n", status);
    } else {              // Child
        printf("ON CHILD\n");

        int fd[2];
        if(pipe(fd) < 0) {
            perror("pipe failed");
            return 1;
        }   

        pid_t pid2 = fork();
        if(pid2 < 0) {
            perror("fork failed");
        }

        if(pid2 > 0) {  // Child -> Parent
            printf("ON CHILD-Parent\n");
            close(fd[1]);
            dup2(fd[0], STDIN_FILENO);
            execlp("sort", "sort", "-r", NULL);
            perror("Problem with execlp");
            return 1;
        } else {          // Child -> Child
            printf("ON CHILD->Child\n");
            close(fd[0]);
            dup2(fd[1], STDOUT_FILENO);
            execlp("ls", "ls", "-a", NULL);
            perror("Problem with execvp");
            return 1;
        }
    }

    printf("\nEnd of program.\n");
}

该程序的另一个问题是@nategoose评论的问题:如果&#34; ls -a&#34;的输出,对waitpid的调用可能导致死锁。太大而无法放入管道的缓冲区。没有理由等待,所以应该简单地将其消除。

答案 1 :(得分:1)

这不是一个真正的答案,但我有一些我想分享的内容。

为了确保您的输出按照它应该的顺序排出,我冲洗的次数比你多了很多。请记住,当您调用fork()clone()vfork()dup()dup2()close()或任何{{}等函数时1}}你正在做的函数系列,它们属于C运行时环境,包括exec()。如果你这样做:

stdio

你很可能会得到:

printf("cat");
fork();
fflush(stdout);

作为你的输出,因为你已经复制了stdout结构,包括所有缓冲的数据,所以除非stdio决定在printf函数结束之前无论如何都要刷新,然后&#34; cat&#34;在每个进程的stdout缓冲区中。

还有一个事实是,当您在catcat 系列中运行某个功能时,数据可以保持缓冲状态,在您的程序被新程序替换之前,您的数据可能无法刷新。当您的程序被execls替换时,stdout中待处理的所有数据都会永远丢失。

此外,当你使用sort时,你有另一个问题,因为你正在从stdio下面交换文件描述符,所以它可能还没有刷新,数据可能最终被刷新到新文件之后DUP。

由于这些事情,你应该拨打更多dup电话,但我不认为这是你的问题。