2

A paragraph from APUE Chapter 5.4 about line buffer:

  1. Line buffered. In this case, the standard I/O library performs I/O when a newline character is encountered on input or output. This allows us to output a single character at a time (with the standard I/O fputc function), knowing that actual I/O will take place only when we finish writing each line. Line buffering is typically used on a stream when it refers to a terminal—standard input and standard output, for example. Line buffering comes with two caveats. First, the size of the buffer that the standard I/O library uses to collect each line is fixed, so I/O might take place if we fill this buffer before writing a newline. Second, whenever input is requested through the standard I/O library from either (a) an unbuffered stream or (b) a line-buffered stream (that requires data to be requested from the kernel), all line-buffered output streams are flushed. The reason for the qualifier on (b) is that the requested data may already be in the buffer, which doesn’t require data to be read from the kernel. Obviously, any input from an unbuffered stream, item (a), requires data to be obtained from the kernel.

I don't really understand the two caveats. Can someone give an example?

2
  • who and why down voted the question? Please explain! Oct 10, 2018 at 23:55
  • I did not downvote the question, but I am guessing that the question lacks particular information. Which parts of the quote you do not understand specifically ? Feb 24, 2019 at 22:39

2 Answers 2

4
  1. Basically says, for example, that if your program is writing text to the terminal, a character at a time, very slowly, and the line is (for example) 520 characters long, then the library might write the first 512 characters to the terminal before your program has finished writing (or even generating) the line.
  2. Says that if your program is writing text to the terminal, a character (or some other small piece) at a time, and then it reads from the terminal (i.e., the keyboard), then the library will write to the terminal the portion of the output line that your program has generated up to that point.  This is usually what you want, as a partial line written to the terminal might be a prompt for input, but sometimes it might not be.
2
  • 2
    @PRY: WTH?  gnis’s edit (1) changed the meaning of my answer (deviated from the original intent of the post), (2) introduced sloppy, non-standard wording, (3) introduced punctuation errors, and (4) had an incorrect edit summary (it says “Improved formatting.”, which it didn’t do, and doesn’t mention the fact that it scrambled what I wrote).  Please don’t approve edits like this — on my posts, or anybody else’s. Feb 25, 2019 at 4:28
  • 2
    @RalfFriedl: WTH?  gnis’s edit (1) changed the meaning of my answer (deviated from the original intent of the post), (2) introduced sloppy, non-standard wording, (3) introduced punctuation errors, and (4) had an incorrect edit summary (it says “Improved formatting.”, which it didn’t do, and doesn’t mention the fact that it scrambled what I wrote). Please don’t approve edits like this — on my posts, or anybody else’s. Feb 25, 2019 at 4:28
1

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:

  1. When an input is requested via unbuffered standard I/O.
  2. 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:

  1. An input is requested to kernel.
  2. 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:

  1. The line buffer on stdin stream is empty, so it requests data from kernel.
  2. kernel flushes all line buffered streams that are opened(which includes “Name?” on stdout).
  3. As the result, “Name?” is printed to the terminal as an output.
  4. 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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.