intricacies/understanding stdio 缓冲区和 dup2

intricacies/understanding the stdio buffer and dup2

我正在阅读这篇文章 lecture 并发现了以下我修改过的代码示例:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>

int main()
{
    int fd;
    char *s, *t;
    off_t ret;

    fd = open("file6", O_WRONLY | O_CREAT | O_TRUNC, 0666);
   
    if (dup2(fd, 1) < 0) { perror("dup2"); exit(1); }
    
    printf("Standard output now goes to file6\n");
    s = "before close\n";
    write(1, s, strlen(s));
    
    close(fd);

    printf("It goes even after we closed file descriptor %d\n", fd);
    
    printf("%ld\t"
        "%ld\n",
        (long int) lseek(fd,0,SEEK_CUR), 
        (long int) lseek(1,0,SEEK_CUR));

    s = "And fwrite\n";
    
    fwrite(s, sizeof(char), strlen(s), stdout);

    printf("%ld\t"
        "%ld\n",
        (long int) lseek(fd,0,SEEK_CUR), 
        (long int) lseek(STDOUT_FILENO,0,SEEK_CUR));
    
    fflush(stdout); 

    s = "And write\n";
    write(1, s, strlen(s));
    
    printf("after:\tAnd wri...: lseek(fd,0,SEEK_CUR)=%ld\t"
        "lseek(STDOUT_FILENO,0,SEEK_CUR)=%ld\n",
        (long int) lseek(fd,0,SEEK_CUR), 
        (long int) lseek(STDOUT_FILENO,0,SEEK_CUR));
    return 0;
}

我共享两个不同的输出,代码中唯一的变化是 fflush(stdout) 行在第一个被注释掉并出现在第二个 运行.

输出(注释 fflush(stdout)):

before close
And write
Standard output now goes to file4
It goes even after we closed file descriptor 3
-1  13
And fwrite
-1  13
after:  And wri...: lseek(fd,0,SEEK_CUR)=-1 lseek(STDOUT_FILENO,0,SEEK_CUR)=23

带 flush(stdout) 的输出未注释:

before close
Standard output now goes to file4
It goes even after we closed file descriptor 3
-1  13
And fwrite
-1  13
And write
after:  And wri...: lseek(fd,0,SEEK_CUR)=-1 lseek(STDOUT_FILENO,0,SEEK_CUR)=127

我有两个问题:

Why does "And write" appear first when fflush(stdout) is commented?

因为 C stdio 缓冲区尚未填满,所以在缓冲区填满、stdio 句柄被刷新或程序结束之前,使用 stdio API 编写的任何内容都不会实际发送到输出。您的直接 write 调用(例如 "And write")完全绕过 stdio 缓冲区,并立即写入,直到程序结束(或者至少,直到 [=11 之后),所有缓冲的内容才会出现=] 已经写过了)。

Why lseek prints -1?

第一个 lseek 是在 fd 上调用的,您在 dup2STDOUT_FILENO/1 上调用它后不久就关闭了,所以它失败了。如果您正确检查了 errno(在每个 lseek 之前将 errno 归零,分别调用两个 lseek 并分别存储或打印它们的错误和 errno,所以它们中的一个在你看到它之前不会覆盖另一个的 errno),你会看到它的值对应于 EBADF,而不是 ESPIPE。 (STDOUT_FILENO) 上的第二个 lseek 工作得很好。代码的轻微修改版本(使用 stderr 因此即使您无法读取实际文件,您也可以看到最后几个输出的输出,每次仔细地将 errno 归零,在调用之前打印它再次 lseek,并使用 strerror 显示 errno) 的友好描述,清楚地表明:Try it online!