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

问题描述 投票:3回答:3

最近出现基本问题时,我一直在阅读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'的功能。每次按下'enter',读取功能都会终止,并将包括'ent'产生的'\ n'在内的字符传递给写入功能。因此它进入循环,并首先打印读取的字符数。


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

> $ ./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

我想一遍又一遍,只是想不通。您能帮我吗?

c file-io eol
3个回答
1
投票

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

因此,似乎所有输出都输出到终端时,stdout缓冲区会自动刷新,但重定向到文件时不会刷新。因此,我建议将通话添加到

fflush(stdout);

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

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


4
投票

write()not缓冲了。write()ing到printf() is已缓冲,但其方式取决于输出的位置。

如果printf()的输出进入控制台,则它是行缓冲的,如果不是,则是完全缓冲,这在第二个示例中导致在程序末尾被刷新,而对stdout的调用的输出]立即出去。

来自stdout

[...]标准输入和输出流已完全当且仅当流不涉及交互时才缓冲设备。

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


2
投票

首先是一个解决方案,将stdoutwrite()更改为write()

man stdio

有关代码的注意事项:

  • 这里使用man stdio是可选的,因为否则您不会从read中读取。
  • write取元素大小和元素数量来确定应写入的数量。部分元素不会被读取,因此element size fread and fwrite(不算1)是文本通常需要的。
  • 错误处理和返回值有所不同,并且函数对fread的设置不是很好定义,有关更多信息,请参见fwrite

  • 最后的简短解释:>输入和输出被缓冲。低级文件描述符IO(#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); } freadstdiofread and fwrite等)未缓冲,并且完全绕过缓冲。

    这些不应混在同一个文件中

,因为即使尝试这样做,也很容易混淆缓冲细节,以便“应”起作用。即使使它在您的OS上运行,为不同的OS和库进行编译时,它可能也会损坏。因此,请不要这样做,而应将一个或另一个用于同一打开的文件。
© www.soinside.com 2019 - 2024. All rights reserved.