Let’s start off with something easy. What is a terminal? Actually, this turns out to be not so easy. I count at least four possible meanings of terminal in a computing context.
First: A terminal(a) or physical terminal (as I might call it) is a hardware device attached to a computer at which humans can interact with that computer. Here are some facts about terminals(a):
- A keyboard, mouse, or monitor on its own is not a terminal, because a keyboard or a mouse can only be used to input data, whereas a monitor can only be used to output data.
- However, a combination of keyboard, mouse, and monitor is a common kind of terminal.
- A touch-screen monitor—at an airline kiosk, say—may be a terminal on its own.
- In a desktop computer, it is generally true that everything that isn’t part of the tower is part of the terminal: keyboard, mouse, joystick, monitor, speakers, printer, scanner.
- Many people, programmers especially, have desktops with more than one monitor. However, these monitors are all part of one terminal. If there were really two terminals, it would be possible for two people to use the computer at the same time.
- Computers that do, in fact, have multiple hardware terminals are quite common in academic and professional environments. Often, these terminals are thin clients, computers that are relatively useless on their own, but have a keyboard, mouse, monitor, maybe one or two USB ports, and a local area connection to a more powerful server machine that actually runs user programs. Many universities have student computer labs that are filled with thin clients.
- In a laptop, the terminal and the non-human-facing parts are fused.
Second: A terminal(b) or virtual terminal, also virtual console, is a virtual hardware device. There may be several virtual terminals on each physical terminal. In fact, on a typical Linux system, there are seven usable virtual terminals by default, and you can add more (more on that later). Even though only one person can use a hardware terminal at any given time, that person may maintain several distinct streams of interaction with the computer, one on each virtual terminal.
This might seem awfully abstract, so I will explain concretely what this means. When I boot up my Ubuntu live USB, by default, the graphical environment will start up. However, if I now press Ctrl+Alt+F1, I will be presented with a black screen identifying itself as “tty1” and asking me to type in my login name. If I press Alt+F2, I will see the same thing, except that it will say “tty2”. Alt+F7 will take me back to the graphical environment. There are a total of seven virtual terminals, numbered 1 through 7, and terminal number 7 runs the graphical environment. Each virtual terminal has its own virtual screen, but only one virtual screen can be on the physical terminal’s monitor at any given time. I can switch to terminal number n by pressing Alt+Fn, although if I am currently in the graphical environment then I must also hold down Ctrl. Formally, Alt+Fn activates virtual terminal number n, which means that virtual terminal number n receives all keyboard and mouse input exclusively (this is not completely true, but I probably won’t get into that), and only that virtual terminal’s virtual screen is shown on the physical terminal.
Now, I can log in on the graphical desktop environment and open a program, say, a web browser, and start downloading a file, and then I can switch to tty1 and log in and get a bash prompt; then I can switch back to the graphical environment and check the progress of my download, and then I can switch back to tty1 to find the bash prompt undisturbed. This is what I meant by “several distinct streams of interaction with the computer”.
Switching virtual terminals is usually done by the user by pressing [Ctrl+]Alt+Fn, but it can also be triggered in software using either
chvt(1) or the Linux-specific system call
ioctl(ttyfd, VT_ACTIVATE, num). Here,
num is the number of the virtual terminal you wish to switch to, and the
ttyfd argument must be a valid file descriptor to a terminal(c)1.
The system console, by the way, is also a virtual terminal. When Linux is in single-user mode, the system console, which is not equivalent to any of the numbered virtual terminals, is the only virtual terminal available. When Linux is not in single-user mode, you cannot switch to the system console (as far as I know).
Third: A terminal(c) or terminal device, also known as a tty, is a Unix device or an associated device special file, usually living in
/dev, that provides a software abstraction to either a virtual terminal or a similar kind of device, in the same way that (for example)
/dev/sda1 might represent a hard drive or
/dev/dsp the speakers. Thus, if you write to
/dev/tty1, for example, characters will appear on the first virtual terminal’s virtual screen, which you will be able to see if you then switch to it (or, immediately, if the first virtual terminal is currently active). The following terminal devices are found on a typical system. You will probably see most of them if you list the contents of
- The system console, with device node
/dev/console(device type 5:1). This is the only terminal that is required to exist by POSIX: see 
- The seven (or more) virtual terminals, with corresponding device nodes
/dev/tty7(4:1, …, 4:7). My Ubuntu system actually goes up to 63 (4:3f).
- The device node
/dev/tty(5:0) refers to the controlling terminal of the process that opens it (more on this later). So if I have bash running on tty1 and tty2, and I launch a program from bash on tty1 and that program opens
/dev/tty, the effect will be identical to if it had opened
/dev/tty1, but if I launched the same program from bash on tty2 then the effect would be identical to if it had opened
/dev/tty2. This device node is also required to exist by POSIX. If a program with no controlling terminal tries to open
/dev/tty, the result (on Linux, at least) will be ENXIO.
- Some systems have a
/dev/tty0(4:0). Now, the virtual terminals are always numbered starting from one, but several of the Linux-specific terminal
ioctl(2)s that take a virtual terminal number as an argument will happily accept 0, which means the currently active virtual terminal. In analogy, opening
/dev/tty0will give you a file descriptor to the currently active virtual terminal. On a headless machine (with, therefore, no active virtual terminal), trying to open this device, again, yields ENXIO.
- You will typically find serial consoles on a Linux system as well; my Ubuntu system has four, with device nodes
/dev/ttyS3(4:40, …, 4:43), regardless of how many serial ports are actually present on the machine. If you read from or write to one of these, the system will attempt to receive or transmit data through a serial port presumably connected to another computer. (If there is no connection, you’ll simply get an EIO.) I probably won’t talk about these much.
- BSD-style pseudoterminals. I will probably devote an entire part to pseudoterminals, and I will not explain what they are right now; however, a pseudoterminal has two parts, a master and slave part, where the master is exposed as the device node
/dev/pty([a-e]|[p-z])[0-f]and the slave
/dev/ptya0and so on: a total of 256 pairs.) The major device number is 2 for each master and 3 for each slave; the minor device numbers start at 00 for
ptyp0, 01 for
ttyp1, and so on, and increase up to af for
ttyzf, at which point they wrap, so b0 for
ttya0, and so on up to ff for
ttyef. (Presumably, it starts at “p” because this is the first letter of “pseudo”, and it wraps around the alphabet just because it has to.)
- Unix98 pseudoterminals, consisting of the master multiplexer
/dev/ptmx(5:2) and the slaves
/dev/pts/ngenerated on demand, that is,
/dev/pts/0(88:0, not sure whether that’s set in stone) and so on.
So, if you didn’t know the purpose of these device nodes, well, you’ll find out quite soon. Most of the following parts will be about terminal devices.
Note that a pseudoterminal is often called a pty, and the term “tty” sometimes includes pseudoterminals and serial consoles, and at other times it includes only the system console and numbered virtual terminals. You’ll have to figure it out by context.
Fourth: A terminal(d), or terminal emulator, is a program that provides an interface similar to a virtual terminal; typically, when you start it, it’ll present you with a command shell at which you can type commands and run programs just as you could on an actual virtual terminal. Familiar examples might include xterm, gnome-terminal, konsole, Terminal.app, and iTerm. I might begrudgingly accept Windows’s
cmd.exe as a terminal emulator too, but I won’t talk about it at all because (as you will see) it is not like the others, in that it does not employ pseudoterminals, which, indeed, do not exist on Windows at all.
A terminal emulator cannot function on its own; in order to actually display anything, it needs an output device like a monitor, and of course you cannot type anything into it unless you have a keyboard. So a terminal emulator cannot function without (as you will see) terminal devices, a virtual terminal, and a physical terminal; a terminal device would be meaningless without a corresponding virtual terminal; and a virtual terminal cannot function without a physical terminal. So, in a fundamental sense—if you’ve been noting the letters I’ve been using with the definitions above—we have terminals(a) at the bottom, supporting terminals(b), supporting terminals(c), supporting terminals(d). In the screenshot above, gnome-terminal is running on the seventh virtual terminal, where all the graphical programs usually go.
That concludes part one. In part two, I will start talking about terminal devices and how they differ from other kinds of special files.
1 Now, you might ask, what terminal device does
ttyfd have to refer to? Shouldn’t this system call only care about one terminal, that is, the one you want to switch to? Well, based on tests I’ve run, the terminal device to which
ttyfd refers must be either one of the numbered virtual terminals (such as the currently active one, or the one you want to switch to) or the system console,
/dev/console. It will not work if you use a pseudoterminal or one of the serial consoles,