dup2、stdout和stderr出现问题

3
当运行此程序时,“stderr”行会在“stdout”行之前显示。为什么?我以为dup2会使stderr和stdout使用相同的文件描述符,所以缓冲区不应该有问题。我正在Solaris 10上使用gcc 3.4.6。
#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;
}
3个回答

7
stdout和stderr的FILE *与文件描述符1和2之间存在差异。在这种情况下,是FILEs导致了您不希望看到的行为。默认情况下,stderr未进行缓冲,因此在出现错误时,您可以以最可靠的方式打印出消息,即使此打印会降低程序的整体性能。
默认情况下,stdout是有缓冲的。这意味着它有一个存储您要写入的数据的内存数组。它会等待直到填充到一定水平的数组(称为缓冲区),或者(如果设置为行缓冲,通常如此)直到看到'\n'。您可以调用fflush(stdout);来立即打印。
您可以更改FILE *的缓冲设置。man 3 setbuf具有为您执行此操作的功能。
在您的示例中,stdout缓冲区保留了字符串"stdout",而"stderr"被写入屏幕。然后,在退出程序时,所有打开的FILE *都会刷新,因此"stdout"随后被打印出来。

5
stdout和stderr这两个流可能使用相同的文件描述符,但在FILE流将任何数据写入其底层文件描述符之前,数据会存储在流的缓冲区中。stdout和stderr中的缓冲区并不因为这两个流连接到同一个文件描述符而变得相同。
请注意,这种缓冲是由stdio库中的FILE流完成的,而不是由操作系统内核及其文件描述符完成的。那里可能还有其他缓冲正在进行,但是这个问题是由stdio库层次引起的。

谢谢。我对流和文件描述符之间的区别感到困惑。 - Doug Masterson

4

清空标准输出怎么办?

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

(刚刚尝试过,它可以工作)

是的,我知道如何解决这个问题。只是我不理解它的行为。如果stdout和stderr使用相同的文件描述符,我认为缓冲不应该是一个问题。 - Doug Masterson
我认为在这方面没有任何精确的标准..我的意思是操作系统内核会按照自己的方式处理事情.. - Jack

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接