Why does redirection (or piping) change the program's behavior

255 views Asked by At

Consider a program that creates a child process printing in an infinite loop, and kills it after one second:

#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;
}

The output, as I expected, was:

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

which makes sense, as the child process probably isn't instantly terminated after the father sends the singal.

However, as soon as I run the program through a pipe, or redirect the output to another file, e.g.

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

The output becomes:

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

That is, it appears to be that there's no output from the child process after the father kills it.

What is the reason for this difference?

1

There are 1 answers

4
Gilles 'SO- stop being evil' On BEST ANSWER

puts uses stdio, which can be buffered. Usually, stdout is line-buffered when it's connected to a terminal, meaning that the buffer is flushed each time a newline is printed. Thus when you run the program without redirecting its output, each line is printed at the time of the puts call. When the program's standard output is redirected to a file or to a pipe, stdout becomes fully buffered: the output data accumulates in the buffer and is only written out when the buffer is full. The program is killed before it has time to fill the buffer, so you don't see any output.

You can confirm that this is what you're observing by calling setvbuf(stdout, NULL, _IOLBF, BUFSIZ) to set stdout to line buffered mode before outputting anything. Then you should see the same amount of lines whether the output is going to a terminal, to a file or to a pipe.

It would be possible to observe other effects too; at this scale, the behavior is very dependent on scheduler fine-tuning. For example it may matter how long your terminal takes to render the ouptut, what other programs are running at the same time, whether the shell that you're running the program from has been doing CPU-intensive or IO-intensive things lately…