C 管道不';不要用叉子循环工作

C 管道不';不要用叉子循环工作,c,pipe,fork,C,Pipe,Fork,工作原理:父级创建一个子级,执行第一个命令并将输出重定向到管道,然后第二个子级将标准输入重定向到该管道。 我的问题是:当第二个孩子试图从管道中读取数据时,它会一直等待 (我确实需要一个循环,因为在我的实际程序中,我不知道有多少命令,所以它只是一个简单的版本) #包括 #包括 #包括 #包括 int main(int argc,字符**argv){ int-fd[2]; 管道(fd); 对于(int i=0;i

工作原理:父级创建一个子级,执行第一个命令并将输出重定向到管道,然后第二个子级将标准输入重定向到该管道。

我的问题是:当第二个孩子试图从管道中读取数据时,它会一直等待
(我确实需要一个循环,因为在我的实际程序中,我不知道有多少命令,所以它只是一个简单的版本)

#包括
#包括
#包括
#包括
int main(int argc,字符**argv){
int-fd[2];
管道(fd);
对于(int i=0;i<2;i++){
如果(fork()==0){
printf(“\n操作%d\n”,i);
如果(i==0){
dup2(fd[1],1);
关闭(fd[0]);
关闭(fd[1]);
printf(“\n执行前%s\n”,argv[1]);
execlp(argv[1],argv[1],NULL);
}
else如果(i==1){
dup2(fd[0],0);
关闭(fd[0]);
关闭(fd[1]);
printf(“\n执行前%s\n”,argv[2]);
execlp(argv[2],argv[2],NULL);
}
出口(0);
}
等待(空);
}
返回(0);
}

除了无法检查函数调用的返回值和处理这些返回值可能指示的错误之外,您的代码看起来相当合理。然而,我确实看到两个问题:

  • 我看到的最重要的问题是,主进程保持其自己的管道端点副本打开。只要至少有一个打开的文件描述引用管道的写入端,在任何进程中,文件的结束都不会被通知到读取端。如果在读取端运行的进程是将其输入读取到完成的进程,例如
    cat
    grep
    sed
    ,那么该进程将永远等待写入端关闭,因为主进程直到该子进程终止才会退出(因为
    wait()

  • 主进程在分叉后立即等待每个子进程,特别是在分叉第二个子进程之前等待第一个子进程。管道有有限的缓冲区,如果第一个子级在完成写入其所有输出之前填充管道缓冲区,那么它将阻塞。在这种情况下,主进程将永远不会启动第二个子进程,否则将耗尽管道


  • 总的来说,管道通常只在两个端点之间提供通信。在这种常见情况下,重要的是在没有不必要延迟的情况下启动读写器,并确保管道端上的所有句柄在所有进程中都是关闭的,除了一个进程的一个线程中有一个读取端副本,另一个线程或不同进程中有一个写入端副本。在像您这样的情况下,避免不必要的延迟包括在两个(所有)子进程都启动之前,不要对任何子进程执行
    wait()
    ing操作。

    如果您按照应该的方式检查函数调用的返回值,那么您将知道故障发生的位置,如果确实存在任何实际故障。第一个孩子在“dup2(fd[1],1)”之后死亡,但我不明白为什么。
    dup2
    的返回值是什么?我倾向于认为第一个孩子要么在
    execlp()
    处失败,要么执行的程序挂起或阻塞,或者确实是执行程序运行到完成并终止。请特别注意,其
    printf()
    的输出将进入管道。您可以
    fprintf()
    stderr
    来避免这种情况。但是,如果任何函数调用失败,您可以从它们的返回值中知道它。对于这些特定功能,您还可以根据
    errno
    ,确定故障的性质,通过
    peror()
    函数(顺便说一句,该函数将其输出发送到
    stderr
    ,而不是
    stdout
    )可以方便地获得相关反馈。
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <sys/wait.h>
    
    int main(int argc, char**argv) {
        int fd[2]; 
    
        pipe(fd);
        for(int i = 0; i < 2; i++) {
            if (fork() == 0) {
                printf("\nloop %d\n", i);
                if (i == 0) {
                    dup2(fd[1], 1);
                    close(fd[0]);
                    close(fd[1]);
                    printf("\nbefore exec %s\n", argv[1]);
                    execlp(argv[1], argv[1], NULL);
                }
                else if(i == 1) {
                    dup2(fd[0], 0);
                    close(fd[0]);
                    close(fd[1]);
                    printf("\nbefore exec %s\n", argv[2]);
                    execlp(argv[2], argv[2], NULL);
                }
                exit(0);
            }
            wait(NULL);
        }
        return(0);
    }