Zum Inhalt

Linux Terminal Architecture: TTY/PTY Kernel Subsystem, Line Discipline, ANSI Escape Sequences & termios

Executive Summary: Most engineers use a terminal emulator every day without distinguishing between the Graphical Terminal Emulator (Alacritty, Kitty, Foot, WezTerm), the Kernel Pseudo-Terminal (PTY) Subsystem (/dev/ptmx & /dev/pts/N), the Line Discipline (N_TTY), and the Command-Line Shell (bash, zsh). This architectural deep dive dissects how keystrokes travel from userspace into the Linux kernel TTY driver, how job-control signals (SIGINT, SIGTSTP, SIGHUP) are generated before bytes ever reach a running process, and how stty/termios control raw vs. canonical mode.

  • PTY Master / Slave Pair (/dev/pts/*)


    Understand how terminal emulators open /dev/ptmx to allocate a bidirectional kernel pipe connecting graphical rendering to /dev/pts/N.

  • Kernel Line Discipline (N_TTY)


    Discover why Ctrl+C, Ctrl+Z, and Backspace are handled inside the kernel's N_TTY line discipline rather than by the shell or application.

  • ANSI / ECMA-48 Escape Codes


    Master CSI sequences (\e[...), alternate screen buffers (smcup/rmcup), 24-bit TrueColor rendering, and bracketed paste mode.

  • termios & stty Raw vs. Cooked


    Inspect and recover corrupted terminal state (reset, stty sane) and distinguish Canonical ("Cooked") buffering from Raw character mode.


1. Architecture: From Keystroke to Process (TTY, PTY & N_TTY)

Historically, a TTY (Teletypewriter, /dev/ttyS0) was a physical RS-232 serial port connected to an electromechanical teleprinter, while /dev/tty1–/dev/tty6 are virtual consoles rendered directly by the Linux kernel's framebuffer/DRM subsystem. When you open a modern graphical terminal (or connect via SSH), the kernel allocates a Pseudo-Terminal (PTY) pair consisting of a PTY Master (ptmx file descriptor held by the terminal emulator or sshd) and a PTY Slave (/dev/pts/0, opened as stdin/stdout/stderr by your shell):

%%{init: {'flowchart': {'wrappingWidth': 600, 'subGraphTitleMargin': {'top': 15, 'bottom': 15}}}}%%
flowchart TB
    subgraph UserspaceLeft["Terminal Emulator / sshd"]
        GUI["Alacritty / Kitty / sshd<br/>Holds Master FD (/dev/ptmx)"]
    end

    subgraph KernelSpace["Linux Kernel TTY Subsystem"]
        Master["PTY Master Driver"]
        LineDisc["N_TTY Line Discipline<br/>Echo, Backspace & Signals<br/>(Ctrl+C -> SIGINT)"]
        Slave["PTY Slave Device<br/>/dev/pts/0"]
    end

    subgraph UserspaceRight["Foreground Process Group"]
        Shell["Zsh / Bash / Neovim<br/>FD 0, 1, 2 -> /dev/pts/0"]
    end

    GUI -->|"Write Keystroke Bytes"| Master
    Master --> LineDisc
    LineDisc -->|"Buffered Line or Signal"| Slave
    Slave -->|"read(0, buf, n)"| Shell
    Shell -->|"write(1, ANSI, n)"| Slave
    Slave --> LineDisc
    LineDisc -->|"ONLCR (\n -> \r\n)"| Master
    Master -->|"Render Glyphs"| GUI

Canonical ("Cooked") Mode vs. Non-Canonical ("Raw") Mode

The kernel's N_TTY line discipline operates in one of two primary modes controlled via the termios struct (tcgetattr / tcsetattr):

Mode Kernel N_TTY Behavior Examples
Canonical ("Cooked") Mode Buffers keystrokes inside the kernel until Enter (\n) is pressed; handles Backspace (VERASE) and Ctrl+W (VWERASE) in-kernel; echoes typed characters back to the PTY Master (ECHO); translates Ctrl+C (0x03) into SIGINT for the foreground process group. Standard cat, python3 input(), read
Non-Canonical ("Raw" / "Cbreak") Mode Disables line buffering (-icanon) and kernel echo (-echo); passes every raw byte (including Ctrl+C, arrow keys, and escape sequences) immediately to the userspace process on every keystroke. nvim, htop, tmux, ssh, zsh (ZLE)

2. Inspecting & Controlling the Terminal Driver with tty and stty

Every interactive session is bound to a specific /dev/pts/N or /dev/ttyN character device. You can inspect its active termios flags, baud rate, window dimensions (TIOCGWINSZ), and control characters using stty -a:

Inspecting & Modifying Kernel Line Discipline Parameters
# 1. Identify the current PTY slave character device and inspect its permissions
tty
ls -l $(tty)
# Example output: crw--w---- 1 nejat tty 136, 0 Nov  6 02:00 /dev/pts/0

# 2. Dump all active termios control characters and line discipline flags
stty -a

# 3. Disable terminal echo before typing a sensitive string, then restore it
stty -echo; read -r SECRET_TOKEN; stty echo; echo ""

# 4. Recover a corrupted terminal after cat-ing a binary file (restores cooked mode & ANSI state)
stty sane && tput rs1

Kernel Control Character Mapping (c_cc Array)

When isig is enabled in stty, the kernel intercepts the following control bytes and dispatches POSIX signals or flow-control actions immediately:

Keybinding ASCII Byte stty Name Kernel Action / Signal Dispatched
Ctrl+C 0x03 (^C) intr Sends SIGINT (Signal 2) to the foreground process group.
Ctrl+\ 0x1C (^\) quit Sends SIGQUIT (Signal 3) and generates a core dump.
Ctrl+Z 0x1A (^Z) susp Sends SIGTSTP (Signal 20) to suspend the foreground job.
Ctrl+D 0x04 (^D) eof Flushes the kernel input buffer (0-byte read() = EOF).
Ctrl+S / Ctrl+Q 0x13 / 0x11 stop / start Legacy XON/XOFF software flow control (freezes/unfreezes output; disable with stty -ixon).

3. ANSI / ECMA-48 Escape Sequences & terminfo (tput)

Terminal emulators interpret in-band Control Sequence Introducer (CSI, \e[ or \033[) byte streams written to stdout to move the cursor, switch between the primary and alternate screen buffers, and render 24-bit RGB colors:

Using ANSI CSI Sequences & Portable terminfo Capabilities via tput
# 1. Print 24-bit TrueColor foreground (\e[38;2;R;G;Bm) and reset (\e[0m)
printf "\033[1;38;2;56;189;248mSovereign Linux Terminal (24-bit RGB)\033[0m\n"

# 2. Query terminal dimensions and color capabilities via the terminfo database
tput lines
tput cols
tput colors

# 3. Switch to the Alternate Screen Buffer (used by vim/htop/less), wait 2s, and restore screen
tput smcup
clear
echo "Inside Alternate Screen Buffer..."
sleep 2
tput rmcup

SSH Unknown $TERM Errors (alacritty, xterm-kitty, foot)

Modern GPU-accelerated terminals set TERM=xterm-kitty, TERM=alacritty, or TERM=foot to advertise advanced capabilities (such as kitty graphics protocol or undercurls). When SSH-ing into a minimal Debian/Ubuntu server that lacks the corresponding /usr/share/terminfo/ entry, tools like top, clear, or nano will fail with Error opening terminal: xterm-kitty. Fix this permanently by compiling the local terminfo entry onto the remote server in one pipeline:

infocmp -x | ssh user@remote-server 'mkdir -p ~/.terminfo && tic -x -'


4. Terminal Multiplexing & Session Persistence (tmux)

When an SSH connection drops, the kernel closes the PTY master file descriptor and sends SIGHUP (Hangup) to the controlling process (the login shell), terminating all child processes attached to that session. Running tmux spawns a persistent background server daemon that owns its own independent /dev/pts/N pseudo-terminals, allowing you to detach (Ctrl+B d) and reattach (tmux attach) across network interruptions.

Essential Production tmux Session Workflow
# 1. Create or attach to a named persistent administrative session
tmux new-session -A -s ops

# 2. List all active background tmux sessions and their PTY attachments
tmux list-sessions
tmux list-panes -a -F "#{session_name}:#{window_index}.#{pane_index} #{pane_tty} #{pane_current_command}"