关于UNIX系统中的无缓冲I / O.

时间:2014-03-15 15:26:10

标签: c file-io eol

当一个基本问题出现时,我最近一直在阅读APUE。 我的代码如下所示


#include <apue.h>
#define BUFFSIZE 4096
int main()
{
    int n;
    char buf[BUFFSIZE];
    while((n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0)
    {
        printf("n is %d\n", n);          //this line is added by me for testing
        if(write(STDOUT_FILENO, buf, n) != n)
            err_sys("write error");  //functions defined by the book to print error message
    }

    if(n < 0)
        err_sys("read error");
    exit(0);
}

编译后,当我运行如下所示的程序时

> $ ./mycat
123456[enter]
n is 7
123456
1234[enter]
n is 5
1234

它似乎根据我的代码结构工作。我不太了解' 输入 '的功能。每当我按下“ 输入 ”时,读取函数终止并将包含由'enter'生成的'\ n'的字符传递给write函数。所以它进入循环内部,首先打印读取的字符数。


但是,以下测试似乎违背了上述内容和代码结构。

> $ ./mycat > data
123456[enter]
1234[enter]
^D
> $ cat data
123456
1234
n is 7
n is 5

似乎程序首先将所有字符写入文件然后打印'n'的值,但是根据我的理解,它应首先打印如下

n is 7
123456
n is 5
1234

我一次又一次地想,只是想不出来。你能帮我吗?

3 个答案:

答案 0 :(得分:3)

write() 缓冲。 printf() stdout 缓存stdout,但在某种程度上取决于输出的位置。

如果write()的输出进入控制台它的行缓冲,如果不是完全缓冲,在第二个例子中导致在程序结束时刷新,而输出形成对man stdio的呼叫立即出去。

来自{{3}}:

  

[...]标准输入和输出流是完全的          当且仅当流不引用交互式时才缓冲          设备

     

引用终端设备的输出流始终是行          默认缓冲;

答案 1 :(得分:2)

首先是一个解决方案,将readwrite更改为fread and fwrite

#include <apue.h>
#define BUFFSIZE 4096
int main()
{
    int n;
    char buf[BUFFSIZE];
    while((n = fread(buf, 1, BUFFSIZE, stdin)) > 0)
    {
        printf("n is %d\n", n);          //this line is added by me for testing
        if(fwrite(buf, 1, n, stdout) != n) {
            // note: if err_sys depend on errno, it may print wrong error
            err_sys("write error");
        }
    }

    if(ferror(stdin)) {
        // note: if err_sys depend on errno, it may print wrong error
        err_sys("read error");
    }
    exit(0);
}

有关代码的注意事项:

  • 此处使用fread是可选的,因为您不会从stdio读取。
  • fread and fwrite获取元素大小和元素数量,以确定应该写多少。部分元素将不会被读取,因此元素大小 1(不计数1)是文本通常需要的。
  • 错误处理和返回值存在差异, stdio 函数的errno设置定义不明确,有关详情,请参阅here

最后简短解释: stdio 输入和输出被缓冲。低级文件描述符IO(openclosereadwrite等)未缓冲,并完全绕过 stdio 缓冲。 这些不应该在同一个文件上混合使用,因为即使你尝试这样做也很容易混淆缓冲细节,因此它应该&#34;应该&#34;工作。即使你让它在你的操作系统上工作,它也可能在为不同的操作系统和库编译时中断。所以不要这样做,而是使用其中一个用于同一个打开的文件。

答案 2 :(得分:1)

有几种缓冲正在进行中。程序的输入由伪终端设备的线路缓冲规程缓冲。在输出端,有一个文件系统缓存(操作系统中用于整个文件的缓冲区),以及在打印到FILE *类型时C程序中的额外缓冲。但是readwrite绕过FILE *缓冲并将数据或多或少地直接移入/移出文件系统缓存。

因此,当所有输出都转到终端时,您的stdout缓冲区会自动刷新,但重定向到文件时则不会。所以我建议添加一个电话

fflush(stdout);
printf致电后的

。这应该显式刷新缓冲区(并强制执行所需的输出顺序)。

需要注意的重要一点是,当您使用FILE *时,这是由库函数(如fopen)操纵的C级结构,以及当您使用FILE时。重新使用原始文件描述符(它只是一个整数,但指的是基础操作系统文件)。 FILE数据类型是这个较低级Unix实现细节的包装器。 {{1}}函数实现了额外的缓冲层,因此较低级别可以在较大的块上运行,并且您可以有效地执行字节类型处理,而无需进行大量的I / O握手。