C stdout重定向到管道的奇怪行为

C stdout重定向到管道的奇怪行为,c,linux,C,Linux,这里有一个简单的例子来说明我的问题。我有一个程序分叉一个新的子进程,并将stdout重定向到它。它很好用。然后我分叉第二个子进程并将stdout重定向到它,然后关闭第一个管道。我希望第一个子流程在其输入管道中接收EOF并终止。相反,它将保持在读取状态,直到主任务退出。我不明白为什么。我希望第一个管道被关闭,第一个子进程变成僵尸 下面是演示该问题的代码: #include <stdio.h> #include <stdlib.h> #include <unistd.h

这里有一个简单的例子来说明我的问题。我有一个程序分叉一个新的子进程,并将stdout重定向到它。它很好用。然后我分叉第二个子进程并将stdout重定向到它,然后关闭第一个管道。我希望第一个子流程在其输入管道中接收EOF并终止。相反,它将保持在读取状态,直到主任务退出。我不明白为什么。我希望第一个管道被关闭,第一个子进程变成僵尸

下面是演示该问题的代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int popenin(char *command) {
    int                 pin[2];
    pid_t               pid;

    if (pipe(pin) != 0) exit(1);

    pid = fork();
    if (pid < 0) exit(1);

    if (pid == 0) {
        close(pin[1]);
        dup2(pin[0], 0);
        close(pin[0]);
        execlp("bash", "bash", "-c", command, NULL);
        perror("Error:");
        exit(1);
    } else {
        close(pin[0]);
        return(pin[1]);
    }
}

int main() {
    int fd;
    fd = popenin("gzip > foo1.gz");
    dup2(fd, 1);
    close(fd);
    printf("foo 1 content\n");fflush(stdout);
    fd = popenin("gzip > foo2.gz");
    close(1);
    dup(fd);
    close(fd);
    printf("foo 2 content\n");fflush(stdout);
    sleep(10000);
}
#包括
#包括
#包括
int popenin(char*命令){
int引脚[2];
pid_t pid;
如果(管道(销)!=0)出口(1);
pid=fork();
如果(pid<0)退出(1);
如果(pid==0){
关闭(引脚[1]);
dup2(引脚[0],0);
关闭(引脚[0]);
execlp(“bash”、“bash”、“-c”、命令,NULL);
佩罗尔(“错误:”);
出口(1);
}否则{
关闭(引脚[0]);
返回(引脚[1]);
}
}
int main(){
int-fd;
fd=popenin(“gzip>foo1.gz”);
dup2(fd,1);
关闭(fd);
printf(“foo 1内容”);fflush(标准输出);
fd=popenin(“gzip>foo2.gz”);
关闭(1);
dup(fd);
关闭(fd);
printf(“foo 2内容”);fflush(标准输出);
睡眠(10000);
}
这个程序创建了两个文件foo1.gz和foo2.gz,它们都是空的,并且系统中运行着两个gzip进程。我希望看到第一个文件完成,关闭,第一个gzip进程退出

如果我用下面的方法修改这个最小的示例,它将按预期工作

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int popenin(char *command) {
    int                 pin[2];
    pid_t               pid;

    if (pipe(pin) != 0) exit(1);

    pid = fork();
    if (pid < 0) exit(1);

    if (pid == 0) {
        close(pin[1]);
        dup2(pin[0], 0);
        close(pin[0]);
        execlp("bash", "bash", "-c", command, NULL);
        perror("Error:");
        exit(1);
    } else {
        close(pin[0]);
        return(pin[1]);
    }
}

int main() {
    int fd;
    fd = popenin("gzip > foo1.gz");
    dup2(fd, 1);
    close(fd);
    printf("foo 1 content\n");fflush(stdout);
    close(1);                    // close(1) is moved before popenin
    fd = popenin("gzip > foo2.gz");
    dup(fd);
    close(fd);
    printf("foo 2 content\n");fflush(stdout);
    sleep(10000);
}
#包括
#包括
#包括
int popenin(char*命令){
int引脚[2];
pid_t pid;
如果(管道(销)!=0)出口(1);
pid=fork();
如果(pid<0)退出(1);
如果(pid==0){
关闭(引脚[1]);
dup2(引脚[0],0);
关闭(引脚[0]);
execlp(“bash”、“bash”、“-c”、命令,NULL);
佩罗尔(“错误:”);
出口(1);
}否则{
关闭(引脚[0]);
返回(引脚[1]);
}
}
int main(){
int-fd;
fd=popenin(“gzip>foo1.gz”);
dup2(fd,1);
关闭(fd);
printf(“foo 1内容”);fflush(标准输出);
close(1);//close(1)在popenin之前移动
fd=popenin(“gzip>foo2.gz”);
dup(fd);
关闭(fd);
printf(“foo 2内容”);fflush(标准输出);
睡眠(10000);
}

有人能解释一下为什么第一个版本不起作用吗?

为什么在第二个
popenin()之后使用
dup()
?在第一次
popenin()
之后,您使用了
dup2()
,这是一种明智的方法。其行为与
dup2
相同。
dup
之所以存在,是因为我可以通过移动一行代码从第一个示例创建第二个示例。@EliasVanOotegem:一般来说不是(您可能会想到
fflush(stdin)
)。但在这种情况下,代码先前已关闭并重新打开fd 1(通过使用
dup2
)。我不会说它是UB,因为C标准对
dup2
一无所知,但这可能不是正确的方法-stdio子系统当然不希望它的FD在背后移动。@EliasVanOotegem我不认为
fflush(stdout)
是UB。无论如何,如果使用
write
而不是
printf
fflush
,问题仍然存在。为什么在第二次
popenin()之后使用
dup()
?在第一次
popenin()
之后,您使用了
dup2()
,这是一种明智的方法。其行为与
dup2
相同。
dup
之所以存在,是因为我可以通过移动一行代码从第一个示例创建第二个示例。@EliasVanOotegem:一般来说不是(您可能会想到
fflush(stdin)
)。但在这种情况下,代码先前已关闭并重新打开fd 1(通过使用
dup2
)。我不会说它是UB,因为C标准对
dup2
一无所知,但这可能不是正确的方法-stdio子系统当然不希望它的FD在背后移动。@EliasVanOotegem我不认为
fflush(stdout)
是UB。无论如何,如果使用
write
而不是
printf
fflush
,问题仍然存在。