标准I / O流 - fgets()缓冲类型

时间:2016-09-02 15:17:50

标签: c unix fgets io-buffering

本书" unix环境中的高级编程"在第15章讨论了 pipe ,它表明在处理标准I / O函数时应该注意缓冲类型。

不同打开的标准I / O流的缓冲类型(在本书的第5章中讨论):

  • 标准错误为unbuffered
  • 连接到终端设备的流是line-buffered
  • 所有其他信息流均为fully-buffered

当父/子连接到pipe时,他们用来通信的结尾(应该是FILE *类型对象,根据接口)应该是fully-buffered根据上面的规则列表(因为它是一个连接到pipe的流)。但是该章中的示例代码的行为似乎不是fully-buffered

以下是示例代码:

myuclc.c:

1   #include "apue.h"
2   #include <ctype.h>

3   int
4   main(void)
5   {
6       int     c;

7       while ((c = getchar()) != EOF) {
8           if (isupper(c))
9               c = tolower(c);
10          if (putchar(c) == EOF)
11              err_sys("output error");
12          if (c == '\n')
13              fflush(stdout);
14      }
15      exit(0);
16  }

popen1.c:

1   #include "apue.h"
2   #include <sys/wait.h>

3   int
4   main(void)
5   {
6       char    line[MAXLINE];
7       FILE    *fpin;
8
9       if ((fpin = popen("myuclc", "r")) == NULL)  // "myuclc" is executable file compile-link by "myuclc.c"
10          err_sys("popen error");
11      for ( ; ; ) {
12          fputs("prompt> ", stdout);
13          fflush(stdout);
14
15          if (fgets(line, MAXLINE, fpin) == NULL) /* read from pipe */
16              break;
17          if (fputs(line, stdout) == EOF)
18              err_sys("fputs error to pipe");
19      }
20      if (pclose(fpin) == -1)
21          err_sys("pclose error");
22      putchar('\n');
23      exit(0);
24  }

所以我的问题是:fgets()第15行的popen1.c根据缓冲规则应该是fully-buffered,为什么它会像line-buffered或{{1}那样行事}:

此外,我还尝试在unbuffered之前setvbuf()专门将缓冲类型设置为fgets()的{​​{1}}(完全缓冲),但仍无效。

_IOFBF

2 个答案:

答案 0 :(得分:3)

在myuclc.c中,您在每个换行符上执行显式刷新:

12          if (c == '\n')
13              fflush(stdout);

这会导致流手动进行行缓冲。无论何时刷新管道,另一端的进程都将被解锁,并将读取当时缓冲区中的任何内容。

&#34;缓冲规则&#34;谈论这种冲洗何时自动发生。每次写入命令(fprintf,fputc等)后,都会自动刷新无缓冲的流。只要将换行符写入流中,就会自动刷新行缓冲流。

当缓冲区填满,流关闭或编写器执行显式刷新时,所有流都会刷新

答案 1 :(得分:0)

您的代码与您的描述不符。您正在谈论pipe系统调用,但代码使用popenpopen是一个不在ISO C中但在POSIX中的函数,并且在POSIX中受其自己的一组要求的约束。不幸的是,POSIX并没有说明popen - ed流的缓冲模式是什么。但是它有这个好奇的措辞:&#34;在打开输入过滤器之前缓冲读数可能会使该过滤器的标准输入错位。通过仔细的缓冲冲洗可以防止输出滤波器的类似问题;例如,使用fflush。&#34; 我无法理解第一句话:如何在开放之前进行阅读?第二句似乎暗示popen流可能是完全缓冲的,因此可能需要显式fflush以确保数据传递到输出管道。当然,如果那个进程本身正在读取带有完全缓冲的输入,那么它可能没有帮助!

如果使用pipe系统调用创建管道,获取一对文件描述符,则可以使用FILE *在这些描述符上创建fdopen个流。这再次不是ISO C功能。因此,它不受ISO C为fopen提供的要求的约束,即:&#34;当打开时,当完全缓冲流时,当且仅当可以确定不引用交互时设备。清除流的错误和文件结束指示符。&#34; 要查看fdopen是否属实,我们必须查看POSIX。不幸的是,POSIX对此保持沉默;它没有说缓冲。它也没有说fdopen继承了fopen的任何特殊要求。它确实说模式标志的含义是&#34;完全按照fopen()中的规定,除了以w开头的模式不会导致文件被截断。&#34;

POSIX有fopen的描述,该描述反映了上面引用的关于缓冲的ISO C文本,逐字逐句。由于POSIX对fdopen的描述没有任何此类文字,并且没有要求fdopen必须遵循fopen的要求(除了关于模式标志的含义),由fdopen设置的缓冲在空中。即使文件描述符是TTY,符合fdopen也可以设置完全缓冲。

因此,如果您正在使用fdopenpopen,并且在您的情况下选择缓冲很重要,则应自行安排setvbuf。< /强>

关于:

  

我还尝试在fgets()之前设置vvf()......

缓冲影响输出stdio输入函数不会延迟缓冲输入数据到应用程序的传递。您可以从连接到管道的进程中读取单独的行,这意味着该进程正在为每条线路刷新自己的输出缓冲区。然后,该线路将通过管道传输,并可供您自己的流程使用。 stdio库不会延迟fgets操作,直到更多行累积,即使在完全缓冲下也是如此。那不是它的工作方式;完全缓冲意味着累积输出直到缓冲区填满或调用fflush