From modes to tty types to control chars and signals, the TTY system is itself a bloated relic whose stink becomes apparent when you try to connect it to the rest of the unix philosophy, where everything is supposed to be a stateless text stream.
This stuff is really interesting for historical reasons and made sense 20 years ago, but I long for the day where the TTY system is gone and character drivers work on some standard JSON stream or something.
Still, I think the TTY - and UARTS in general - have a lot of utility in this day and age, warty dragons and all. I've got a terminal server in my lab just for the case of wiring up the small, light, cheap, efficient embedded systems which still depend on the dragons to deliver the fire. Nothing quite like having a functional 9600-baud term you can tap into with an Oscilloscope and work out what the bits mean .. try doing that with a JSON-based interface. ;)
Further, there are still things for which there is not a right way to do it. If you look at the man page for "unbuffer" (part of expect), there's a "CAVEATS" section that describes a bug... that can't be fixed while PTYs are in their current state. I wouldn't call it "well and truly solved".
By 1994, GUIs were common on workstations, and hardware-based serial terminals were disappearing fast. OTOH, the command line as a UI paradigm is alive and well today and might well be around for another 20 or 40 years.
I have a system where shell users run commands that can grab some locks, and they like to press ^Z to suspend them which isn't compatible with a simple fcntl-based lock.
In your case, there should be a way to trap attempted Ctrl-Z suspends in your process and break the locks (or do whatever you need to do).
It gives a thorough walk through of the ttys and pty's, IMHO it is a must read for anybody wanting do to TTY programming, and it is also a great source for understanding how the streams works in connection with the tty's/pty's.
Actually this is backwards.
Line discipline is a historical artifact. Typically mainframes had IO front ends (AKA channel controllers) than managed IO so that the CPU (which BTW was what was charged for) could be kept busy and not wait around for users (by contrast CTSS, ITS and later machines like the Alto expressed an alternative, radical idea which was that the computer was there to support the user). Basically once a line was ready it was submitted by pressing the enter key and then its process was unblocked and processed the data. (Nowadays we just queue an event on the main thread...essentially the same thing!).
Likewise, a number of early network protocols like X.25 would charge per "line" (more about that later).
Unix was originally written for the PDP-7 (& later 11) which were minicomputers that didn't have channel controllers (in fact the earliest arpanet interfaces that weren't IMPS were PDP-11s acting as networking channel controllers for PDP-10 mainframes). Since IO was done by the kernel, you didn't want to to an expensive context switch for the common case of just reading a character and putting it into an input buffer.
So despite the comment quoted above, the true Unix model would have been for the kernel to have been agnostic and for each program to decide how it wanted to handle character input. But that would have been too expensive, so a more pragmatic approach prevailed.
By the way there are all sorts of other fossils in the TTY system. Multics used # to delete a character and @ to rub out the whole line -- a legacy of printing characters that persisted into Unix for years ###### decades. This seems weird today, but originally Multics (and later Unix) didn't have traditional line disciplines, or even control characters, much less select(). So it was handy to be able to essentially do immediate mode editing right in the input buffer.
Another fossil is that vi is just a visual mode grafted onto ed (clone of Multics qed IIRC), which was an editor with commands based on line-at-a-time IO.
ITS got proper IO virtualization first (and the predecessor to select, and extended it to the network with SUPDUP, later also implemented by Multics and eventually Unix, which made using Emacs possible) and actually had a more general version of select() before Unix was written, but at the time it was considered exotic.
Oh and the X.25 comment? Well in 1982 I was working in France and the only way for me and my colleagues who had come from MIT to get back to our mail at the MIT AI lab was a connect across the ocean on an international X.25 network hooked into MIT Multics and thence connect to the AI lab. After using fully interactive systems, it was painful to use a line-at-a-time system, so I figured out how to reset the X.25 front end to ship a whole packet for every character typed (packets had a maximum length so if you typed a really long line it would send a packet's worth, so I hacked it into thinking that a packet length was 1). Which was great, until shortly after the end of the month when someone "upstairs" had a heart attack at the size of the networking bill...
// headers for terminal control
// global variable
struct termios saved_attributes;
// prototype of atexit callback
void reset_input_mode (void);
// --- inside main()
// local variable
struct termios termios_data;
// tell stdio not to buffer output
setvbuf(stdout, NULL, _IONBF, 0);
// incantation to the terminal
saved_attributes = termios_data;
termios_data.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN);
termios_data.c_cc[VMIN] = 1;
tcsetattr(0, TCSANOW, &termios_data);
// set atexit callback so we can restore the prior terminal mode upon exit
// --- outside main()
// implementation of atexit callback -- restores terminal to prior mode
void reset_input_mode (void)
tcsetattr (STDIN_FILENO, TCSANOW, &saved_attributes);
Every virtual terminal in the Linux text-mode console also requires a TTY/PTY pair.
Shell mode (and more generally any mode derived from Comint mode) in Emacs also requires a TTY/PTY pair: although the Emacs documentation claims that Comint-derived modes can be configured to use a pipe instead, a comment in comint.el confesses that no one has gotten that to actually work.
And of course, SSHing into a remote host requires at least one TTY/PTY pair (on the remote host).
Are there other place in the software on, e.g., a Mac reliant on a TTY? If there are, please tell me what they are!
The `expect` suite of tools uses a TTY/PTY pair to script programs that might otherwise be unhappy with not having a PTY. Same with the `script` program, which on my system is part of the 'util-linux' package.
On GNU/Linux, `systemd-nspawn` (a chroot-like tool) uses a TTY/PTY pair to isolate the child process from the outside world.
The problem arose from two later, non-Unix (and arguably deliberately anti-Unix¹) influences.
One, as the Debian notes linked in a sibling comment mentions, was GNU Emacs, whose bindings came from custom non-ASCII, bucky-bit² devices, and gleefully stepped on ASCII control characters like Backspace (^H) and Device Control 3 (^S).
The other was DEC's new VT2x0 series of terminals, with the VMS-oriented LK201 keyboard³, that hid Backspace and Escape on unlabelled function keys (F12 and F11 respectively) and made the key in the typist's backspace position send DEL. (The VT100⁴ had had both Backspace, in the typist's location, and DEL, where US PC keyboards have \|.) This was the big one, as BSD 4.2 for the VAX spread and dragged the LK201 along with it.
This was one of the things that convinced me to start using Debian more than 15 years ago. They, like no other Unix at the time, had looked really deeply into the problem and come up with a good configuration to best fit all programs and systems. I, as a system administrator who was familiar with this problem, was very impressed by their solution, and I deemed it better than the one I had (which I have since forgotten).