16

The TTY and Terminal

The terminal you type into has a history that goes back to mechanical teletypes.

Open a terminal on your Linux system. You see a dark window with a blinking cursor, waiting for you to type. This seems simple -- you type, the computer responds. But between your keyboard and the program reading your input, there is a chain of abstraction layers with roots going back to the 1800s.

The word "terminal" and the abbreviation "TTY" both come from physical machines that no longer exist. Understanding where they came from explains why the system works the way it does today.

From Teletypes to Terminals

In the 1960s and 1970s, computers did not have monitors. Users interacted with the machine through a teletype (also called a teleprinter or TTY) -- a mechanical device with a keyboard and a printer. You typed a command on the keyboard. The computer's response was printed on paper.

The teletype communicated with the computer over a serial line. Each keystroke was converted to an electrical signal (using a code called ASCII), sent down the wire, and received by the computer. The computer's response traveled back the same way, driving the print mechanism.

Key term: TTY Short for "teletype" or "teletypewriter." Originally a physical printing terminal connected to a computer via a serial line. In modern Linux, TTY refers to any terminal device -- physical or virtual -- that provides a text interface between a user and the system.

By the late 1970s, teletypes were replaced by video terminals -- screens with keyboards. The DEC VT100 (1978) became the standard. Instead of printing on paper, it displayed text on a CRT screen. But it still connected to the computer over a serial line, and the computer still treated it as a TTY.

The physical terminals eventually disappeared too, replaced by terminal emulators -- software programs like GNOME Terminal, Alacritty, or xterm that simulate a VT100 (or its successors) in a window on your screen. But the kernel still uses the same abstraction it used for physical teletypes. That abstraction is the TTY subsystem.

Fig. 16a -- Evolution of terminals
1960s-1970s Teletype Mechanical printer Serial line to host Paper output 1978-1990s Video Terminal CRT screen (VT100) Serial line to host Screen output 1990s-today Terminal Emulator Software window PTY to kernel Pixel output Kernel TTY Subsystem Same abstraction layer across all three eras
The hardware changed three times, but the kernel abstraction remained. Modern terminal emulators still speak the same protocol that physical teletypes used.

Virtual Consoles

Before looking at terminal emulators, there is a simpler kind of terminal built directly into the kernel: the virtual console. If you press Ctrl+Alt+F1 through Ctrl+Alt+F6 on a Linux system, you switch between virtual consoles. Each one is a full-screen text terminal, managed entirely by the kernel without any graphical environment.

These are the /dev/tty1 through /dev/tty6 devices. They are real TTY devices -- the kernel draws text directly to the video card's framebuffer. When your graphical desktop fails to start, these consoles are your fallback.

Your graphical desktop typically runs on one of the higher-numbered virtual consoles (often tty2 or tty7, depending on the distribution). The terminal emulator you open within your desktop is something different -- it uses a pseudoterminal.

The Pseudoterminal (PTY)

A terminal emulator like GNOME Terminal or Alacritty is a regular graphical application. It draws a window, renders text using a font, and handles keyboard input. But the shell running inside it expects to talk to a TTY device. How does a regular program pretend to be a hardware terminal?

The answer is the pseudoterminal (PTY) -- a pair of virtual devices created by the kernel. One end is the master (also called the PTY master or ptmx). The other end is the slave (also called the PTY slave or pts).

Key term: Pseudoterminal (PTY) A pair of virtual character devices that simulate a serial terminal connection. The master side is held by the terminal emulator. The slave side is given to the shell. Data written to one end appears as input on the other, with the kernel's line discipline processing in between.

The terminal emulator opens the master side. The shell (and any programs the shell runs) open the slave side. When you type a key in the terminal emulator, the emulator writes the character to the master. The kernel's TTY layer processes it and makes it available for reading on the slave. When the shell prints output, it writes to the slave. The kernel passes it to the master, and the terminal emulator renders it on screen.

You can see your current PTY with:

tty

This will print something like /dev/pts/0. The number increases for each terminal you open. List all active pseudoterminals:

ls /dev/pts/
Fig. 16b -- The pseudoterminal pair
Terminal Emulator (gnome-terminal, alacritty, xterm) /dev/ptmx (master) Line Discipline (echo, editing, signals) /dev/pts/0 (slave) bash (shell) stdin/stdout/stderr

keyboard input screen output

keystrokes output userspace kernel userspace SSH uses the same PTY mechanism -- sshd holds the master, the remote shell gets the slave
The PTY pair connects a terminal emulator to a shell through the kernel. The line discipline sits in the middle, processing input and output.

The Line Discipline

Between the master and slave sides of the PTY sits the line discipline -- a kernel module that processes characters as they flow through the terminal. The line discipline is what makes a terminal feel like a terminal rather than a raw data pipe.

In its default mode (called canonical mode or cooked mode), the line discipline provides:

Line editing. When you type a character and press backspace, the line discipline removes the last character. You are editing a line buffer inside the kernel. The program on the other end does not see any of this -- it only receives a complete line when you press Enter.

Echo. When you type a character, the line discipline sends a copy back to the terminal so you can see what you typed. This is why you see your own keystrokes. Turn off echo (as passwd does), and you type blind.

Signal generation. Certain key combinations are intercepted by the line discipline and converted to signals:

  • Ctrl+C generates SIGINT (interrupt the foreground process)
  • Ctrl+Z generates SIGTSTP (suspend the foreground process)
  • Ctrl+\ generates SIGQUIT (quit with core dump)
  • Ctrl+D on an empty line signals end-of-file

These key bindings are not hard-coded -- they are configurable with the stty command. You can see the current settings:

stty -a
The line discipline is the invisible layer that makes terminals usable. It handles line editing, character echo, and signal generation. Programs can disable it (raw mode) when they need to handle every keystroke themselves.

Raw Mode

Some programs need to handle every keystroke directly -- text editors like vim, interactive programs like top, and anything that uses arrow keys for navigation. These programs switch the terminal to raw mode (technically, they disable canonical mode and echo). In raw mode, the line discipline does nothing: every keystroke is passed immediately to the program without buffering, editing, or signal translation.

This is why pressing Ctrl+C in vim does not kill vim. Vim has put the terminal in raw mode and handles Ctrl+C itself.

How Keystrokes Reach Your Shell

Let us trace what happens when you press the letter "a" in a terminal emulator:

  1. Your operating system's window manager detects the key press and sends a key event to the terminal emulator application.
  2. The terminal emulator translates the key event to the byte 0x61 (ASCII for "a").
  3. The terminal emulator writes 0x61 to the PTY master (/dev/ptmx).
  4. The kernel's line discipline receives the byte. In canonical mode, it adds it to the line buffer and echoes it back to the master (so the terminal emulator can display it).
  5. When you press Enter (0x0A), the line discipline marks the line as complete.
  6. The shell, reading from the PTY slave (/dev/pts/0), receives the complete line.
  7. The shell processes the command and writes its output to the PTY slave.
  8. The output passes through the line discipline (which does minimal processing on output) to the PTY master.
  9. The terminal emulator reads the output from the master and renders it on screen.
Fig. 16c -- Journey of a keystroke
1. Key press detected 2. Encode to ASCII 0x61 3. Write to PTY master 4. Line discipline buffers 5. Enter: line complete 6. Shell reads from slave 7. Shell writes output 8. Emulator renders text Echo Path Line discipline echoes each character back to the master so the emulator can display it BEFORE the shell sees anything emulator kernel (TTY layer) shell emulator
A keystroke makes a round trip: from the emulator to the kernel, through the line discipline, to the shell, and back. Echo happens before the shell ever sees the character.

Terminal Escape Codes

A terminal does more than display plain text. It can position the cursor, change text color, clear the screen, and scroll regions. It does all of this through escape sequences -- special byte sequences that the terminal interprets as commands rather than text.

An escape sequence begins with the ESC character (byte 0x1B), followed by [, then parameters and a command letter. These are called ANSI escape codes because they were standardized by ANSI (the American National Standards Institute) in the 1970s.

Some examples:

SequenceEffect
ESC[2JClear the entire screen
ESC[HMove cursor to top-left
ESC[31mSet text color to red
ESC[0mReset all formatting
ESC[10;20HMove cursor to row 10, column 20
ESC[1AMove cursor up one line

When a program like ls --color outputs colored filenames, it is embedding these escape sequences in its output. The terminal emulator intercepts them and changes the rendering instead of displaying the raw bytes.

Key term: Escape sequence A series of bytes beginning with ESC (0x1B) that instructs a terminal to perform an action like moving the cursor, changing colors, or clearing the screen. Programs use these to create interactive text interfaces.

This is also why piping colored output to a file produces garbled-looking text -- the file contains the raw escape sequences, which only make sense to a terminal.

# This shows escape codes as visible text
ls --color=always | cat -v

The Terminal and Job Control

The terminal layer is deeply connected to process management. Each terminal has a concept of a foreground process group -- the set of processes currently allowed to read from the terminal and receive keyboard signals.

When you press Ctrl+C, the kernel does not send SIGINT to every process. It sends it to the foreground process group of the controlling terminal. When you press Ctrl+Z, SIGTSTP goes to the foreground process group.

This is how job control works in the shell. When you run a command, the shell puts it in the foreground. When you press Ctrl+Z, it gets stopped. When you type bg, the shell moves it to a background process group. Background processes that try to read from the terminal receive SIGTTIN and get stopped -- they are not allowed to compete with the foreground for terminal input.

Fig. 16d -- Terminal sessions and process groups
/dev/pts/0 controlling tty Session (SID 1401) bash (1401) leader Foreground group grep sort Background group make -j4 & Ctrl+C Background cannot read from terminal (SIGTTIN)

SIGINT SIGTSTP

A terminal session contains one foreground and zero or more background process groups. Keyboard signals only reach the foreground group.
The TTY subsystem is not just about displaying text. It manages which processes can read from the terminal, delivers keyboard-generated signals, and provides line editing. Understanding this layer explains why Ctrl+C, Ctrl+Z, and job control work the way they do.

SSH and Remote Terminals

SSH uses the same PTY mechanism. When you SSH into a remote machine, the sshd daemon on the remote side allocates a PTY pair. sshd holds the master, and the remote shell gets the slave. Your keystrokes travel encrypted over the network to sshd, which writes them to the master. Output travels back the same way.

This is why interactive programs work over SSH -- the remote shell sees a real PTY slave and behaves exactly as if you were sitting at a local terminal. The TERM environment variable (usually set to xterm-256color or similar) tells programs which escape sequences the terminal understands.

Inspecting Your Terminal

A few commands for examining the terminal system:

tty                  # Print your current terminal device
stty -a              # Show all terminal settings
who                  # Show logged-in users and their terminals
w                    # Show who is logged in and what they are doing
ls -la /dev/pts/     # List active pseudoterminals
echo $TERM           # Show the terminal type

The stty command is particularly useful. It shows the current line discipline settings: which characters trigger signals, whether echo is on, the terminal dimensions (rows and columns), and the baud rate (a historical artifact that is always 38400 for PTYs).

Why This Still Matters

Every time you open a terminal, type a command, press Ctrl+C to interrupt something, or SSH into a server, the TTY subsystem is doing the work. The abstraction is so good that you rarely think about it. But when something goes wrong -- when a program leaves your terminal in a broken state, when Ctrl+C does not work, when characters display as garbage -- understanding the PTY, the line discipline, and escape codes gives you the knowledge to fix it.

If your terminal ever gets into a bad state (no echo, garbled display), the fix is:

reset

This sends the terminal reset escape sequence and restores the line discipline to sane defaults. It works because it operates at the same level as the problem -- the TTY layer.

Next: Users, Groups, and Permissions