Line Discipline

Press Ctrl-D in a program that is reading the terminal and it will often stop, so it is easy to believe the key sends an end of file. It sends one byte, 0x04, and with the terminal in its usual setting the program never receives it. The kernel keeps what you type in a line of its own until something releases it. Enter releases the line with a newline on the end. Ctrl-D releases it as it is and is thrown away. If the line was empty, the program's read() gets nothing at all, zero bytes, and zero bytes is how a terminal says end of file. After hi, the same key hands over hi, and the program goes on reading.

New to terminals and read()? Start here

A program does not read the keyboard. It reads its standard input by calling read(), which asks the kernel for up to some number of bytes and is told how many it got. When that input is a terminal, part of the kernel stands between the keys and the program.

That part is the line discipline. In its usual setting, canonical mode, it collects what you type into a line, lets you correct the line, and hands it over only when the line is finished. Until then no program can see any of it.

read() reports the end of its input by returning zero bytes. End of file is that zero. It is not a character stored anywhere, and this page is about the one key that makes it happen.

A computer only has numbers

There are no letters in a computer, no colours and no sound. There are numbers, and an agreement about what a given number means. The letter A is a particular number because a committee said so, and for no other reason.

That agreement is an encoding, and the interesting part is never the table. It is what the table costs: how many bits each symbol takes, which symbols were favoured, what happens to the ones nobody thought of, and whether you can start reading in the middle. Every machine in this topic is an argument about that cost, settled differently.

The machine for this idea on its own is ASCII, if you would rather press it than read about it.

Machines here that come first: ASCII, The System Call.

From a key to a read(), through a line no program can see

1 The key, and the one byte the terminal sends for it

A terminal does not tell the kernel which key was pressed. It sends a byte, or for some keys a few, and the bytes are all the kernel ever learns. A letter sends its own code. Ctrl-D sends 0x04 and Ctrl-U sends 0x15, because Ctrl clears the top two bits of the letter's code, which is the rule ASCII is about. Enter sends a carriage return, 0x0d. Backspace has no byte of its own: the terminal sends one of two, DEL or BS, depending on how it was set up.

Type with these keys. Your own keyboard is not used, because a browser may keep Ctrl-D and Ctrl-U for itself.

Or start from one of these:

keys pressed
3
the last key
Ctrl-D
the byte it sent
0x04, EOT

Ctrl-D sent 0x04, EOT: the letter with its top two bits cleared. That one byte is all the kernel learns about the key.

2 That byte, edited into a line no program can see yet

The byte goes to the line discipline, the part of the kernel between a terminal and the programs reading it. In canonical mode it does not pass bytes on as they come. It keeps them in a line of its own, and a few bytes are instructions about that line rather than part of it. POSIX: “The ERASE character shall delete the last character in the current line, if there is one.” And: “The KILL character shall delete all data in the current line, if there is any.” Neither byte reaches a program, and neither can reach into a line that has already been released.

keybyteit didthe line after
h0x68, "h"added"h"
i0x69, "i"added"hi"
Ctrl-D0x04, EOTreleased the line, dropped itselfnothing
still waiting in the line
nothing
which is
0 bytes

Nothing is waiting in the line.

3 The line, released by Enter or by Ctrl-D

With the default settings, either of two bytes releases the line. A newline goes with it, so the program sees where the line ended. The end-of-file character does not go with it. The Seventh Edition manual, January 1979: “When an EOT is received, all the characters waiting to be read are immediately passed to the program, without waiting for a new-line, and the EOT is discarded.” Then the sentence the whole page is about: “Thus if there are no characters waiting, which is to say the EOT occurred at the beginning of a line, zero characters will be passed back, and this is the standard end-of-file indication.”

linereleased byits byteshow many
1Ctrl-D"hi"2
lines released
1
released empty
0

The last Ctrl-D released 2 bytes, "hi", with no newline, and was thrown away. A program gets those bytes and has no reason to think its input is over.

4 The read() that returns the line, or returns nothing

A program reading the terminal calls read(), which is a system call, the request into the kernel that System Call is about. The kernel answers from the released lines. However many bytes the program asks for, POSIX says “at most one line shall be returned”, and a line released empty is answered with zero bytes, which the program takes as the end of its input.

callreturnsthe bytesfrom line
12 bytes"hi"1
read() calls that returned
1
returned zero bytes
0
the next read()
waits for the next line

Every read() returned bytes. Nothing here has told the program its input is over.

Asking for fewer bytes changes how many calls it takes and nothing else. POSIX says any number may be requested “without losing information”, and the kernel this page was checked against keeps that in every case its transcripts hold at a smaller size, the zero-byte reads included.

These ran in this browser when the page loaded. Each claim, whether it held, and the number behind it.

Each claim, whether it held, and the values behind it
claimheldmeasured
Ctrl-D on an empty line makes read() return zero bytesyes0 bytes
after hi, the same key makes read() return the two bytes hi, and no end of fileyes"hi"
pressed twice after hi, the second Ctrl-D is the one a program reads as the endyes"hi", then 0 bytes
after Enter, one Ctrl-D is enough, because the line it would flush is emptyyes"hi\n", then 0 bytes
over all 37,448 sequences of up to five keys, a read() returns zero bytes exactly when Ctrl-D found the line emptyyes13,437 zero-byte reads, 13,437 empty flushes
and in none of them does the program receive the Ctrl-D, the erase or the kill byteyes0 reads hold 0x04, 0 hold an editing byte
no read() returns more than one line, however many bytes it asks foryes0 reads run past a newline, asking for 4096, 2 or 1 bytes
asking for one byte at a time loses nothing: the same bytes and the same ends of fileyes37,448 of 37,448 agree
Backspace takes the x back before any program sees ityes"hi\n"
and cannot reach back past a line Ctrl-D has already releasedyes"hi", then "\n"
Ctrl-U throws the whole waiting line awayyes"hi\n"
Enter sends a carriage return, 0x0d, and the program reads a newline, 0x0ayes0x0d typed, "\n" read
a Backspace that sends BS to a line discipline expecting DEL puts a ^H in the lineyes"hi^H\n"
and after stty erase ^H the same key erasesyes"h\n"
Ctrl-D is 0x04 and Ctrl-U is 0x15: each letter with its top two bits clearedyesD is 0x44 and U is 0x55; the ASCII bit explains the rule

What is real here, and what is not

The engine was checked against a real kernel, not against itself

The engine on this page was compared with Linux, not with itself. A script opens a pseudo-terminal, writes the bytes a key would send into one end, calls read() on the other, and records what comes back, in order. There are 14,425 cases: every sequence of up to three keys from a nine-byte alphabet, under both ERASE settings and with reads of 4096, 1, 2 and 3 bytes; every sequence of four, under the default settings with 4096-byte reads; the cases named on this page; and a fixed-seed sample of longer runs. The engine agrees with every one. The checks table above runs the engine in this browser, not the kernel. The transcripts are kept with the kernel they came from, Linux 7.0.0-31-generic, and the terminal settings they were made under. One setting was changed to make them: VEOL, an extra line delimiter that is off by default, was set to a byte no case contains, so that the script could tell when the kernel had finished with the keys.

Canonical mode only, and a shell prompt is not a test of it

Everything here is canonical mode, where the kernel assembles lines. A program can switch it off and read each byte as it arrives, and nothing on this page describes that. Bash reads its command line through GNU Readline, which handles the end-of-file character itself. Its manual: “If this character is read when there are no characters on the line, and point is at the beginning of the line, Readline interprets it as the end of input”. So Ctrl-D at a bash prompt tests Readline. A program that simply reads its standard input tests the kernel.

Signals, and Linux's extra keys, are refused by name

Ctrl-C, Ctrl-\ and Ctrl-Z send signals, and Ctrl-S and Ctrl-Q stop and restart output. Linux adds Ctrl-W, which erases a word, Ctrl-V, which makes the next byte ordinary, and Ctrl-R, which reprints the line. None of the three is in POSIX. The page offers none of these keys, and its engine refuses a sequence containing one rather than treating the byte as a letter. Ctrl-O is not refused. The Linux termios manual lists it as discard, “not supported under Linux”, and the kernel this page was checked against hands it to the program as an ordinary byte. So does the engine.

Which byte erases is a setting, not a key

A new Linux terminal's ERASE is DEL, 0x7f, and the transcripts record it. Some terminals send BS, 0x08, for Backspace, and for those the line discipline has to be told, with stty erase ^H. Leave the two disagreeing and Backspace puts a ^H into the line, which the program then reads. The two settings in the first two steps are there to show exactly that, and both combinations are in the transcripts.

In 1979 the erase key was #, and DEL was the interrupt key

The Seventh Edition's defaults, from its tty.h: erase is #, kill is @, end of file is 004, and interrupt is 0177, which is DEL. The key that erases on a Linux terminal now is the one that sent an interrupt then, and the interrupt has moved to Ctrl-C. The page runs Linux's defaults and prints the 1979 ones only here.

A backslash protected # and @ in 1979. On Linux it does not

The Seventh Edition let a backslash before the erase or kill character make it ordinary, and its tty.c skips the erase, kill and end-of-file checks whenever the byte before is a backslash. POSIX permits this on systems that claim its XSI option. Linux does not do it: in the transcripts, a backslash followed by DEL erases the backslash, and a backslash followed by Ctrl-D is released with the backslash still in it. There is no backslash key on this page, so the difference never comes up in the steps above.

The Seventh Edition edited the line when a program read it

Linux applies erase and kill as each byte arrives. The Seventh Edition kept the raw bytes, with a marker after each newline and end of file, and did the editing in canon(), which runs when a program reads. For the keys on this page, reading that code gives the same bytes either way. That is one reading of the code, not a run: there is no Seventh Edition kernel behind this page.

A line has a limit, and the page does not draw it

The Seventh Edition manual: “Currently this limit is 256 characters.” And: “When the input limit is reached all the saved characters are thrown away without notice.” Linux does something else. On the kernel these transcripts came from, 5,000 letters and then a newline came back as one read() of 4,096 bytes, the first 4,095 letters and the newline, and the rest were dropped. That was measured on one kernel, it is recorded with the transcripts, and the engine does not model it.

Why 1979, when the sentence is older

The two sentences quoted in the third step are word for word in the Sixth Edition manual of May 1975, and the mechanism is older than that. This page does not say when it began. 1979 is the Seventh Edition, where the end-of-file character became a setting: the Sixth Edition's tty.c compares each byte with a fixed CEOT, and the Seventh's compares it with t_eofc, which a program can change. That setting is what POSIX calls VEOF. The name is in the 1979 source as well, where tty.h gives each terminal a t_line and comments it line discipline.

No echo is drawn

You see what you type because the line discipline writes it back to the terminal, and that echo has rules of its own. This page shows the line as the kernel holds it, not the screen, and makes no claim about the echo.

Sound: no

Asked and answered. Nothing here has a duration to hear. A click per key would decorate a table that already prints every byte, and the terminal bell is a byte a program writes, not part of reading a line.

Sources