代码之家  ›  专栏  ›  技术社区  ›  Xaphanius

分叉进程后的Exec不会在程序完成之前返回结果

  •  1
  • Xaphanius  · 技术社区  · 11 年前

    我正在尝试创建一个程序,该程序分叉一次,当父级等待子级终止时,该子级再次分叉,然后执行两个exec。程序上有一个Pipe,我已经检查了程序上每个dup2()和Pipe()的返回值-只是在这里省略了它们,使其看起来更简洁-。问题是,我只在程序完成后得到ls-a | sort-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 回复  |  直到 11 年前
        1
  •  3
  •   Community Mohan Dere    9 年前

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

    如果关闭父进程中的管道描述符,或者更好地移动 调用第一个分叉进程,然后当最后一个进程退出时,排序进程将终止,该进程的管道写端具有打开的描述符( 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 对以下内容发表了评论: waitpid 如果“ls-a”的输出太大,无法放入管道的缓冲区,则可能导致死锁。没有理由等待,所以应该简单地消除它。

        2
  •  1
  •   nategoose    11 年前

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

    为了确保你的输出按照应有的顺序输出,我比你冲洗了很多。请记住,当您调用以下函数时 fork() , clone() , vfork() , dup() , dup2() , close() ,或 exec() 您正在执行的功能系列低于C运行时环境,包括 stdio 。如果您这样做:

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

    你很可能会得到:

    catcat
    

    作为输出,因为您复制了stdout结构,包括所有缓冲数据,所以除非stdio决定在printf函数结束之前进行刷新,否则“cat”在每个进程的stdout缓冲区中。

    此外,由于在 exec 在用新程序替换程序之前,您的数据可能不会被刷新。当您的程序被替换为 ls sort 那么stdout中挂起的任何数据都将永远丢失。

    此外,当您使用 dup 您还有另一个问题,因为您正在从stdio下交换文件描述符,所以它可能还没有刷新,数据可能会在dup后被刷新到新文件。

    因为这些事情,你应该有更多的电话 fflush ,但我认为这不是你的问题。