How does a Linux terminal work?
Originally you had just dumb terminals - at first actually teletypewriters (similar to an electric typewriter, but with a roll of paper) (hence /dev/tty - TeleTYpers), but later screen+keyboard-combos - which just sent a key-code to the computer and the computer sent back a command that wrote the letter on the terminal (i.e. the terminal was without local echo, the computer had to order the terminal to write what the user typed on the terminal) - this is one of the reason why so many important Unix-commands are so short. Most terminals were connected by serial-lines, but (at least) one was directly connected to the computer (often the same room) - this was the console. Only a select few users were trusted to work on "the console" (this was often the only "terminal" available in single-user mode).
Later there also were some graphical terminals (so-called "xterminals", not to be confused with the xterm
-program) with screen & graphical screen-card, keyboard, mouse and a simple processor; which could just run an X-server. They did not do any computations themselves, so the X-clients ran on the computer they were connected to. Some had hard disks, but they could also boot over the network. They were popular in the early 1990s, before PCs became so cheap and powerful.
Later still, there were "smart" or "intelligent" terminals. Smart terminals have the ability to process user input (line-editing at the shell prompt like inserting characters, removing words with Ctrl-W
, removing letters with Ctrl-H
or Backspace
) without help from the computer. The earlier dumb terminals, on the other hand, could not perform such onsite line-editing. On a dumb terminal, when the user presses a key, the terminal sends/delegates the resulting key-code to the computer to handle. After handling it, the computer sends the result back to the dumb terminal to display (e.g. pressing Ctrl-W
would send a key-code to the computer, the computer would interpret that to mean "delete the last word", so the computer would handle that text change, then simply give the dumb terminal the output it should display).
A "terminal emulator" – the "terminal-window" you open with programs such as xterm
or konsole
– tries to mimic the functionality of such smarter terminals. Also programs such as PuTTY
(Windows) emulate these smart terminal emulators.
With the PC, where "the console" (keyboard+screen) and "the computer" is more of a single unit, you got "virtual terminals" (on Linux, keys Alt+F1 through Alt+F6) instead, but these too mimic old-style terminals. Of course, with Unix/Linux becoming more of a desktop operating system often used by a single user, you now do most of your work "at the console", where users before used terminals connected by serial-lines.
It's of course the shell that starts programs. And it uses the fork
system-call (C language) to make a copy of itself with a environment-settings, then the exec
system-call is used to turn this copy into the command you wanted to run. The shell suspends (unless the command is run in the background) until the command completes. As the command inherits the settings for stdin, stdout and stderr from the shell, the command will write to the terminal's screen and receive input from the terminal's keyboard.
When you “open a terminal”, you're starting a terminal emulator program, such as xterm, gnome-terminal, lxterm, konsole, …
One of the first things the terminal emulator does is to allocate a pseudo terminal (often called a pseudo-tty, or pty for short). The pty is a pair of character device files: the pty master, which is the side that the terminal emulator opens, and the pty slave, which is the side that programs running inside the terminal have open. On most modern unices, the master is /dev/ptmx
(which every terminal emulator has open) and the slave is /dev/pts/NUMBER
. The kernel driver for pseudo-terminals keep track of which process controls the master for each slave device. The terminal emulator can retrieve the path to the corresponding slave through an ioctl on the master device.
Once the terminal emulator has opened the master device, it starts a subprocess (typically a shell, but it's up to the user who invoked the terminal emulator to decide). The emulator does this in the usual way to invoke a program:
- fork a child process,
- open the slave pty device on file descriptors 0, 1 and 2 (standard input, standard output and error stream),
- execute the shell or other program in the child process.
When the child (or any other process) writes to the pty slave, the emulator sees input on the pty master.
Conversely, when the emulator writes to the master device, it is seen as input on the slave.
Expect works in exactly the same way. The difference between Expect and a terminal emulator such as xterm is where they get the input that they feed the program (script vs keyboard input) and what they do with the output (log file or parser vs drawing text in a window).