为什么重定向(或管道)会改变程序的行为

考虑一个程序,它在无限循环中创建子进程打印,并在一秒后终止它:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>

int main(void) {
    pid_t pid = fork();

    if (pid == 0) {
        while (1)
            puts("I'm still alive");
    } else {
        sleep(1);
        puts("Dispatching...");
        kill(pid, SIGTERM);
        puts("Dispatched!");
    }

    return 0;
}

正如我所料,输出是:

I'm still alive
I'm still alive
...
Dispatching...
I'm still alive
I'm still alive
...
Dispatched!

这是有道理的,因为在父亲发出信号后,子进程可能不会立即终止.

但是,只要我通过管道运行程序,或者将输出重定向到另一个文件,例如,

$./prog | tail -n 20
$./prog > out.txt

输出变为:

I'm still alive
I'm still alive
...
Dispatching...
Dispatched!

也就是说,在父亲杀死子进程之后,似乎没有来自子进程的输出.

造成这种差异的原因是什么?

最佳答案 puts使用
stdio,可以是
buffered.通常,当stdout连接到终端时,它是行缓冲的,这意味着每次打印换行时都会刷新缓冲区.因此,当您在不重定向其输出的情况下运行程序时,每次行都会在puts调用时打印.当程序的标准输出重定向到文件或管道时,stdout变为完全缓冲:输出数据在缓冲区中累积,仅在缓冲区已满时写出.程序在有时间填充缓冲区之前被杀死,因此您看不到任何输出.

您可以通过调用setvbuf(stdout,NULL,_IOLBF,BUFSIZ)来确认这是您正在观察的内容,以便在输出任何内容之前将stdout设置为行缓冲模式.然后,您应该看到相同数量的行,无论输出是转到终端,还是文件或管道.

也可以观察其他效果;在这种规模下,行为非常依赖于调度程序的微调.例如,终端渲染输出所需的时间,其他程序同时运行的程序,您运行程序的shell是否最近一直在进行CPU密集型或IO密集型操作可能很重要…

点赞