Trouble with dup2, stdout, and stderr

11,919

Solution 1

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.

Solution 2

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.

Solution 3

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)

Share:
11,919

Related videos on Youtube

Doug Masterson
Author by

Doug Masterson

Updated on May 06, 2022

Comments

  • Doug Masterson
    Doug Masterson almost 2 years

    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;
    }
    
  • Doug Masterson
    Doug Masterson over 13 years
    Yes, I know how to fix the problem. I just don't understand the behavior. If stdout and stderr use the same file descriptor, I would think buffering shouldn't be a problem.
  • Jack
    Jack over 13 years
    I don't think there are any precise standard about that.. I mean that the OS kernel will handle things internally as it wants anyway..
  • Doug Masterson
    Doug Masterson over 13 years
    Thanks. I was confused about the difference between streams and file descriptors.