代码之家  ›  专栏  ›  技术社区  ›  Joseph Sible-Reinstate Monica

为什么fread循环需要额外的Ctrl+D来用glibc向EOF发送信号?

  •  12
  • Joseph Sible-Reinstate Monica  · 技术社区  · 6 年前

    通常,要在Linux终端上向附加到标准输入的程序指示EOF,如果只按Enter,则需要按Ctrl+D一次,否则需要按两次。我注意到 patch 不过,命令是不同的。有了它,我需要按两次Ctrl+D如果我只是按Enter,或三次否则(做 cat | patch 反而没有这种奇怪。另外,如果我在输入任何真正的输入之前按Ctrl+D,就不会有这种奇怪的现象了 补丁 the way it loops on fread . 这里有一个最小的程序可以做同样的事情:

    #include <stdio.h>
    
    int main(void) {
        char buf[4096];
        size_t charsread;
        while((charsread = fread(buf, 1, sizeof(buf), stdin)) != 0) {
            printf("Read %zu bytes. EOF: %d. Error: %d.\n", charsread, feof(stdin), ferror(stdin));
        }
        printf("Read zero bytes. EOF: %d. Error: %d. Exiting.\n", feof(stdin), ferror(stdin));
        return 0;
    }
    

    当完全按照原样编译和运行上述程序时,以下是事件的时间表:

    1. 弗雷德 .
    2. 呼叫 read 系统调用。
    3. 我键入“asdf”。
    4. 这个
    5. 呼叫 系统再次调用。
    6. 阅读 系统调用返回0。
    7. 返回5。
    8. 我的程序打印 Read 5 bytes. EOF: 1. Error: 0.
    9. 我的程序调用 再一次。
    10. 弗雷德 呼叫 系统调用。
    11. 我再次按Ctrl+D。
    12. 这个 阅读 系统调用返回0。
    13. 返回0。
    14. 我的程序打印 Read zero bytes. EOF: 1. Error: 0. Exiting.

    为什么这种读取stdin的方法有这种行为,不像其他程序读取它的方式?这是一个窃听器吗 补丁 ? 应该如何编写这种循环来避免这种行为?

    更新: readv 代替 阅读 ).

    现在,问题变成了:glibc的行为是错误的,还是patch错误地认为libc不会有这种行为?

    1 回复  |  直到 6 年前
        1
  •  6
  •   Joseph Sible-Reinstate Monica    6 年前

    我已经设法确认这是由于2.28(commit)之前的glibc版本中的一个明确错误造成的 2cc7bad the C standard :

    这个 字节输入/输出函数 输入/输出:[…], fread

    呼叫 fgetc 功能。

    如果流位于文件结尾,则设置流的文件结尾指示符,并且 fgetc公司 EOF . 否则 函数返回 stream

    (强调“或”我的)

    fgetc公司

    #include <stdio.h>
    
    int main(void) {
        while(fgetc(stdin) != EOF) {
            puts("Read and discarded a character from stdin");
        }
        puts("fgetc(stdin) returned EOF");
        if(!feof(stdin)) {
            /* Included only for completeness. Doesn't occur in my testing. */
            puts("Standard violation! After fgetc returned EOF, the end-of-file indicator wasn't set");
            return 1;
        }
        if(fgetc(stdin) != EOF) {
            /* This happens with glibc in my testing. */
            puts("Standard violation! When fgetc was called with the end-of-file indicator set, it didn't return EOF");
            return 1;
        }
        /* This happens with musl in my testing. */
        puts("No standard violation detected");
        return 0;
    }
    

    要演示错误,请执行以下操作:

    1. 编译程序并执行它
    2. 按Ctrl+D

    确切的错误是,如果设置了文件流结尾指示符,但流不在文件末尾,glibc的fgetc将返回流中的下一个字符,而不是标准要求的EOF。

    弗雷德 定义为 fgetc公司 glibc bug #1190 自提交后已修复 2CC7负载