Doug Masterson
Doug Masterson

Reputation: 81

Trouble with dup2, stdout, and stderr

When this program is run, the "stderr" line is displayed before the "stdout" line. Why? I thought dup2 would make stderr and stdout use the same file descriptor so there should be no problem with buffering. I'm using gcc 3.4.6 on Solaris 10.

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

int main()
{
    int fd[2];
    int pid;
    char buf[256];
    int n;

    if(pipe(fd) < 0) {
        perror("pipe");
        return 1;
    }
    if((pid = fork()) < 0) {
        perror("fork");
        return 1;
    }
    else if(pid > 0) { // parent
        close(fd[1]);
        if((n = read(fd[0], buf, sizeof(buf))) > 0) {
            buf[n] = 0;
            printf("%s", buf);
        }
    }
    else {
        dup2(fd[1], fileno(stdout));
        dup2(fd[1], fileno(stderr));
        close(fd[1]);
        fprintf(stdout,"stdout\n");
        fprintf(stderr,"stderr\n");
    }
    return 0;
}

Upvotes: 3

Views: 13305

Answers (3)

nategoose
nategoose

Reputation: 12382

There is a difference between the FILE *s stdout and stderr and the file descriptors 1 and 2. In this case it is the FILEs that are causing the behavior that you weren't expecting. stderr is not buffered by default so that in the case of an error you can print out the message in the most reliable manner, even though the performance of this printing slows down overall performance of the program.

stdout, by default, is buffered. This means that it has an array of memory that it is storing the data that you told it to write in. It waits until it has the array (called a buffer) filled to a certain level or (if it is setup for line buffering, which it often is) until it sees a '\n'. You could call fflush(stdout); to make it go ahead and print, though.

You can change the buffering settings of FILE *. man 3 setbuf has the functions that do this for you.

In your example the stdout buffer was holding the string "stdout" while the "stderr" was being written to the screen. Then upon exiting the program all of the open FILE * are flushed, so "stdout" then got printed.

Upvotes: 7

Thomas Padron-McCarthy
Thomas Padron-McCarthy

Reputation: 27632

The two streams stdout and stderr may be using the same file descriptor, but before a FILE stream writes any data to its underlying file descriptor, the data is stored in the stream's buffer. The buffers in stdout and stderr don't become the same just because the two streams are connected to the same file descriptor.

Note that this buffering is done by the FILE streams in the stdio library, not by the OS kernel and its file descriptors. There may be other buffering going on there too, but this problem is caused by the stdio library level above.

Upvotes: 5

Jack
Jack

Reputation: 133567

What about flushing stdout?

dup2(fd[1], fileno(stdout));
dup2(fd[1], fileno(stderr));
close(fd[1]);
fprintf(stdout,"stdout\n");
fflush(stdout);
fprintf(stderr,"stderr\n");

(just tried and it works)

Upvotes: 4

Related Questions