xiao wang
xiao wang

Reputation: 145

stdout stream changes order after redirection?

These days I was learning the "apue", a result of an typical case confused me. The following are the sample codes of "sample.c":

#include "apue.h"
#include <stdio.h>
#define BUFF_SZ 4096

int main()
{
    int n = 0;
    char buff[BUFF_SZ] = {'\0'};
    while ((n = read(STDIN_FILENO, buff, BUFF_SZ)) > 0) {
        printf("read %d bytes\n", n);
        if (write(STDOUT_FILENO, buff, n) != n) {
            err_sys("write error");
        }
    }
    if (n < 0) {
        err_sys("read error");
    }
    return 0;
}

After compilation gcc sample.c, you can use this command echo Hello | ./a.out and get the following std output on terminal:

read 6 bytes
Hello

However, if you redirect the output to a file echo Hello | ./a.out > outfile, then use cat outfile to see the content:

Hello
read 6 bytes

The ouput changes order after redirection! I wonder if some one could tell me the reason?

Upvotes: 3

Views: 114

Answers (3)

9y7h0n
9y7h0n

Reputation: 326

The problem is that the writes are handled by write(2) call, so you effectively lose control of what happens. If we look at the documentation for write(2) we can see that the writes are not guaranteed to be actually written until a read() occurs. More specifically:

A successful return from write() does not make any guarantee that data has 
been committed to disk. In fact, on some buggy implementations, it does not even 
guarantee that space has successfully been reserved for the data. The only way to 
be sure is to call fsync(2) after you are done writing all your data. 

This means that depending on the implementation and buffering of the write(2) (which may differ even between redirects and printing to screen), you can get different results.

Upvotes: 0

Peter
Peter

Reputation: 36597

printf(), by default, buffers its output, while write() does not, and there is no synchronisation between then.

So, in your code, it is possible that printf() stores its data in a buffer and returns, then write() is called, and - as main() returns, printf()s buffer is flushed so that buffered output appears. From your description, that is happening when output is redirected.

It is also possible that printf() writes data immediately, then write() is called. From your description, that happens when output is not redirected.

Typically, one part of redirection of a stream is changing the buffer - and therefore the behaviour when buffering - for streams like stdout and stdin. The precise change depends on what type of redirection is happening (e.g. to a file, to a pipe, to a different display device, etc).

Imagine that printf() writes data to a buffer and, when flushing that buffer, uses write() to produce output. That means all overt calls of write() will have their output produced immediately, but data that is buffered may be printed out of order.

Upvotes: 2

Yu Hao
Yu Hao

Reputation: 122383

For the standard I/O function printf, when you output to a terminal, the standard output is by default line buffered.

printf("read %d bytes\n", n);

\n here cause the output to flush.

However, when you output to a file, it's by default fully buffered. The output won't flush unless the buffer is full, or you explicitly flush it.


The low level system call write, on the other hand, is unbuffered.

In general, intermixing standard I/O calls with system calls is not advised.

Upvotes: 4

Related Questions