本书" 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
答案 0 :(得分:3)
在myuclc.c中,您在每个换行符上执行显式刷新:
12 if (c == '\n')
13 fflush(stdout);
这会导致流手动进行行缓冲。无论何时刷新管道,另一端的进程都将被解锁,并将读取当时缓冲区中的任何内容。
&#34;缓冲规则&#34;谈论这种冲洗何时自动发生。每次写入命令(fprintf,fputc等)后,都会自动刷新无缓冲的流。只要将换行符写入流中,就会自动刷新行缓冲流。
当缓冲区填满,流关闭或编写器执行显式刷新时,所有流都会刷新
答案 1 :(得分:0)
您的代码与您的描述不符。您正在谈论pipe
系统调用,但代码使用popen
。 popen
是一个不在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
也可以设置完全缓冲。
因此,如果您正在使用fdopen
或popen
,并且在您的情况下选择缓冲很重要,则应自行安排setvbuf
。< /强>
关于:
我还尝试在fgets()之前设置vvf()......
缓冲影响输出。 stdio
输入函数不会延迟缓冲输入数据到应用程序的传递。您可以从连接到管道的进程中读取单独的行,这意味着该进程正在为每条线路刷新自己的输出缓冲区。然后,该线路将通过管道传输,并可供您自己的流程使用。 stdio
库不会延迟fgets
操作,直到更多行累积,即使在完全缓冲下也是如此。那不是它的工作方式;完全缓冲意味着累积输出直到缓冲区填满或调用fflush
。