Before exploring two caveats, there are three things to keep in mind.
Unbuffered stream invokes a system call on every
Whereas line buffered stream invokes a system call on every line(whenever the buffer encounters a newline).
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.
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.
For the second caveat, the key point here is the interaction with
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
- 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
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
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
“Name?” has no newline, and is under the limit of 10 bytes.
The standard I/O library does not issue a
write call yet.
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 flushes all line buffered streams that are opened(which includes “Name?” on
- 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.