Before exploring two caveats, there are three things to keep in mind.
Unbuffered stream invokes a system call on every read
or write
.
Whereas line buffered stream invokes a system call on every line(whenever the buffer encounters a newline).
Also, stdin
and stdout
are both standard I/O stream and are typically line buffered.
These streams are opened automatically when a process starts.
Lastly, while APUE is the bible for learning UNIX, some portions are outdated. I do not claim that my answer is most up-to-date. I will try to take examples from the book so that your questions are properly answered in the context of the textbook.
First caveat
As your quote has stated, there is a restriction on how long a line can be.
And surpassing the limit produces the same effect as having a newline.
Before jumping in with an example on the first caveat, we must remember that write
system call actually queues(APUE Chapter 3 Section 6, p86).
Let’s say we have a program named Jasmine
that writes a single character to stdout
every 30 seconds.
Jasmine
writes “Hello!\n”(7 bytes), and it takes 210 seconds to complete.
Remember that the buffer has a restriction.
If our buffer is limited to 10 bytes, the kernel can wait for all 210 seconds and issue a single write
call at the end(after the newline is encountered).
This is an expected behavior.
If the buffer is limited to 4 bytes, however, the kernel will issue a write
call after 4 bytes(“Hell”), then another after the newline(“\n”).
This is the first caveat.
A user might expect a single write
to occur(“Hello!\n” being printed on the terminal).
This is not the case as we’ve saw in our example.
Depending on how busy the system is, the user might see two outputs printing to the terminal within the period of 210 seconds.
This is because even though the stream(stdout
) is line buffered, the content of the line is over the restriction(4 bytes limit).
On every 4 bytes, the standard I/O library will invoke write
as if a newline is encountered.
The example had 7 bytes(“Hello!\n”) in total, and this resulted two write calls.
Second caveat
For the second caveat, the key point here is the interaction with kernel
.
kernel
will act differently based on our interaction with it, resulting another “unexpected” write with line buffers.
Here is the second caveat from your quote:
kernel
will flush all line-buffered streams on two occasions:
- When an input is requested via unbuffered standard I/O.
- When an input is requested via line buffered standard I/O.
We can reword this in the following:
kernel
will flush all line-buffered streams the following prerequisites are satisfied:
- An input is requested to
kernel
.
- The request is through either unbuffered or line buffered standard I/O stream.
The first requirement is that “the data must be requested from kernel”.
This allows an exception on the the line buffered standard I/O.
The exception is that kernel
will not flush if the request does not require any data from kernel
.
This could be a case when the line buffer has all the data stored in the buffer already.
As a quick side example, say a buffer in line buffered stream has “World” characters.
And the user(caller) requests to read 1 byte from the stream.
For the stream to return the requested, single byte(“W”), it does not require any help from kernel
.
In such case, kernel
will not flush any line buffered streams.
With this exception in mind, let’s continue with the example from the first caveat.
(We should keep the buffer limit as 10 bytes)
After outputting “Hello!\n”, our program, Jasmine
, writes "Name?" to stdout
.
“Name?” has no newline, and is under the limit of 10 bytes.
The standard I/O library does not issue a write
call yet.
Then Jasmine
requests an input from stdin
(another line buffered stream), and the following occurs:
- The line buffer on
stdin
stream is empty, so it requests data from kernel
.
kernel
flushes all line buffered streams that are opened(which includes “Name?” on stdout
).
- As the result, “Name?” is printed to the terminal as an output.
kernel
waits for user’s input(keyboard) on the terminal.
This is the second caveat.
From the user’s point of view, seeing “Name?” on the terminal could be confusing as no write
should have been issued yet. Unfortunately, kernel
does issues write
call on all currently-opened line buffered streams before kernel
attempts to fetch the requested data. This is because the line buffered stream and the interaction with kernel
have satisfied both prerequisites.