Bin Bin - 3 months ago 29
C Question

Standard I/O stream -- fgets() buffering type

The book "advanced programming in unix environment" discussed pipe in chapter 15, it shows that we should pay attention to the buffering type when deal with standard I/O functions.

The buffering types for different opened standard I/O streams are (discussed in chapter 5 of the book):


  • standard error is
    unbuffered

  • streams connected to terminal devices is
    line-buffered

  • all other streams are
    fully-buffered



When parent/child connect to a
pipe
, the end (which should be a
FILE *
type object, according to the interface) that they used to communicate should be
fully-buffered
according to the rule list above (since it's a stream connected to
pipe
). But the behavior of sample code from that chapter seems to be something NOT
fully-buffered
.

Here is the sample code:

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 }


So my question is :
fgets()
in line 15 of
popen1.c
should be
fully-buffered
according to the buffering rules, why does it act like
line-buffered
or
unbuffered
:

Furthermore, I also tried to
setvbuf()
before
fgets()
to specifically set buffering type to
_IOFBF
(fully-buffered) of
fpin
, still not work.

prompt> abc
abc
prompt> ABC
abc
prompt> efg
efg
prompt> EFG
efg
prompt>

Answer

In myuclc.c you perform an explicit flush on every newline:

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

This causes the stream to be manually line-buffered. Whenever you flush the pipe, the process on the other end will be unblocked and will read whatever was in the buffer at the time.

The "buffering rules" talk about when this flushing happens automatically. Unbuffered streams are automatically flushed after every write command (fprintf, fputc, etc.). Line-buffered streams are automatically flushed whenever a newline character is written to the stream.

And all streams are flushed when the buffer fills up, when the stream is closed, or when the writer performs an explicit flush