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

时间:2014-11-27 22:09:39

标签: c unix process io-redirection piping

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

#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!

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

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

1 个答案:

答案 0 :(得分:2)

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

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

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