《unix环境下的高级编程》一书在第15章讨论了管道,这表明我们在处理标准I/O函数时应该注意缓冲类型。
不同开放标准 I/O 流的缓冲类型为(本书第 5 章中讨论):
- 标准错误
无缓冲
- 连接到终端设备的流是
行缓冲
- 所有其他流都是
全缓冲
当父/子连接到管道
时,它们用于通信的末端(根据接口(interface),应该是FILE *
类型对象)应该是根据上面的规则列表完全缓冲
(因为它是连接到管道
的流)。但是该章中的示例代码的行为似乎不是完全缓冲
。
这里是示例代码:
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 }
所以我的问题是:根据缓冲规则,popen1.c
第15行中的fgets()
应该是完全缓冲
,为什么它的行为类似于行缓冲
或无缓冲
:
此外,我还尝试在 fgets()
之前使用 setvbuf()
将缓冲类型专门设置为 _IOFBF
(全缓冲) fpin
,还是不行。
prompt> abc
abc
prompt> ABC
abc
prompt> efg
efg
prompt> EFG
efg
prompt>
最佳答案
在 myuclc.c 中,您对每个换行符执行显式刷新:
12 if (c == '\n')
13 fflush(stdout);
这会导致流被手动行缓冲。每当您刷新管道时,另一端的进程将被解除阻塞,并将读取当时缓冲区中的所有内容。
“缓冲规则”讨论了这种刷新何时自动发生。无缓冲流在每个写入命令(fprintf、fputc 等)后自动刷新。每当将换行符写入流时,行缓冲流就会自动刷新。
当缓冲区填满、关闭流或编写器执行显式刷新时,所有流都会被刷新
关于c - 标准I/O流——fgets()缓冲类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39295791/