Writing an OS Kernel from Scratch – Part 21: Implementing a Terminal — Make Your Shell Truly Interactive!
“Your shell can run commands, but how does the user edit commands, handle backspace, or respond to Ctrl+C? Today, we implement a full terminal (Terminal) supporting line editing, signals, and job control!”
In the previous post, we implemented basic commands like ls, cat, shell, but the shell’s input experience was very primitive:
- No ability to edit already-typed characters
- Backspace displayed as garbage
- Ctrl+C couldn’t terminate programs
- No command history
All this is because we were missing a Terminal. The terminal isn’t just an “input/output window” — it’s the intelligent intermediary between the user and the shell.
The Three-Layer Architecture of a Terminal
| Layer | Responsibility | Implementation |
|---|---|---|
| Hardware | Serial/VGA/USB keyboard | Existing UART/VGA drivers |
| TTY Driver | Line buffering, echo, signal handling | ✅ Kernel TTY |
| Terminal Emulator | Cursor movement, colors, window | ✅ Userspace term |
Key concepts:
- TTY (Teletypewriter): A character device in the kernel (e.g., /dev/tty0)
- Terminal Emulator: Userspace program (like xterm, GNOME Terminal)
- Shell: Command interpreter running on the terminal
The TTY driver converts raw hardware input into structured line data and handles special characters (like Ctrl+C).
Kernel TTY Driver Design
The TTY driver core is a state machine handling three modes:
- Raw mode: Pass characters directly (like Vim)
- Canonical mode: Line buffering, handle backspace/enter (like Shell)
- Signal mode: Detect Ctrl+C, Ctrl+Z, etc.
The TTY driver transforms raw character streams into “complete lines” and handles control characters.
Testing: Interactive Shell Experience
With the TTY driver in place, the shell can now handle:
- Line editing with Backspace
- Ctrl+C signal delivery
- Proper echo of typed characters
- Newline-aware input buffering
Comments