đŸ Archived View for crm114.space âș reading-list âș 29d629d012b7dde3a8fc22816b68e8d2.gmi captured on 2023-01-29 at 02:39:12. Gemini links have been rewritten to link to archived content
-=-=-=-=-=-=-
â©ïž Go back to the list of web bookmarks
†℠†℠†℠†℠†℠†â„
Beware, though: What you are about to see is not particularly elegant. In fact, the TTY subsystem â while quite functional from a user's point of view â is a twisty little mess of special cases. To understand how this came to be, we have to go back in time.
Meanwhile, however, the computers â still quite large and primitive, but able to multitask â were becoming powerful enough to be able to interact with users in realtime. When the command line eventually replaced the old batch processing model, teletypes were used as input and output devices, because they were readily available on the market.
There was a plethora of teletype models around, all slightly different, so some kind of software compatibility layer was called for. In the UNIX world, the approach was to let the operating system kernel handle all the low-level details, such as word length, baud rate, flow control, parity, control codes for rudimentary line editing and so on. Fancy cursor movements, colour output and other advanced features made possible in the late 1970s by solid state video terminals such as the VT-100, were left to the applications.
In present time, we find ourselves in a world where physical teletypes and video terminals are practically extinct. Unless you visit a museum or a hardware enthusiast, all the TTYs you're likely to see will be emulated video terminals â software simulations of the real thing. But as we shall see, the legacy from the old cast-iron beasts is still lurking beneath the surface.
A user types at a terminal (a physical teletype). This terminal is connected through a pair of wires to a UART (Universal Asynchronous Receiver and Transmitter) on the computer. The operating system contains a UART driver which manages the physical transmission of bytes, including parity checks and flow control. In a naĂŻve system, the UART driver would then deliver the incoming bytes directly to some application process. But such an approach would lack the following essential features:
Incidentally, the kernel provides several different line disciplines. Only one of them is attached to a given serial device at a time. The default discipline, which provides line editing, is called N_TTY (drivers/char/n_tty.c, if you're feeling adventurous). Other disciplines are used for other purposes, such as managing packet switched data (ppp, IrDA, serial mice), but that is outside the scope of this article.
Together, a particular triplet of UART driver, line discipline instance and TTY driver may be referred to as a TTY device, or sometimes just TTY. A user process can affect the behaviour of any TTY device by manipulating the corresponding device file under /dev. Write permissions to the device file are required, so when a user logs in on a particular TTY, that user must become the owner of the device file. This is traditionally done by the login(1) program, which runs with root privileges.
The physical line in the previous diagram could of course be a long-distance phone line:
This does not change much, except that the system now has to handle a modem hangup situation as well.
Let's move on to a typical desktop system. This is how the Linux console works:
The TTY driver and line discipline behave just like in the previous examples, but there is no UART or physical terminal involved anymore. Instead, a video terminal (a complex state machine including a frame buffer of characters and graphical character attributes) is emulated in software, and rendered to a VGA display.
The console subsystem is somewhat rigid. Things get more flexible (and abstract) if we move the terminal emulation into userland. This is how xterm(1) and its clones work:
To facilitate moving the terminal emulation into userland, while still keeping the TTY subsystem (session management and line discipline) intact, the pseudo terminal or pty was invented. And as you may have guessed, things get even more complicated when you start running pseudo terminals inside pseudo terminals, Ă la screen(1) or ssh(1).
Now let's take a step back and see how all of this fits into the process model.
These attributes are used for job control.
The following example illustrates the relationship between processes, jobs and sessions:
The basic idea is that every pipeline is a job, because every process in a pipeline should be manipulated (stopped, resumed, killed) simultaneously. That's why kill(2) allows you to send signals to entire process groups. By default, fork(2) places a newly created child process in the same process group as its parent, so that e.g. a ^C from the keyboard will affect both parent and child. But the shell, as part of its session leader duties, creates a new process group every time it launches a pipeline.
The TTY driver keeps track of the foreground process group id, but only in a passive way. The session leader has to update this information explicitly when necessary. Similarly, the TTY driver keeps track of the size of the connected terminal, but this information has to be updated explicitly, by the terminal emulator or even by the user.
Now let's take a closer look at how the TTY drivers, the line disciplines and the UART drivers in the kernel communicate with the userland processes.
UNIX files, including the TTY device file, can of course be read from and written to, and further manipulated by means of the magic ioctl(2) call (the Swiss army-knife of UNIX) for which lots of TTY related operations have been defined. Still, ioctl requests have to be initiated from processes, so they can't be used when the kernel needs to communicate asynchronously with an application.
So a signal is a crude mechanism that allows the kernel to communicate asynchronously with a process. Signals in UNIX aren't clean or general; rather, each signal is unique, and must be studied individually.
As you can see, signals are numbered starting with 1. However, when they are used in bitmasks (e.g. in the output of ps s), the least significant bit corresponds to signal 1.
Suppose that you are editing a file in your (terminal based) editor of choice. The cursor is somewhere in the middle of the screen, and the editor is busy executing some processor intensive task, such as a search and replace operation on a large file. Now you press ^Z. Since the line discipline has been configured to intercept this character (^Z is a single byte, with ASCII code 26), you don't have to wait for the editor to complete its task and start reading from the TTY device. Instead, the line discipline subsystem instantly sends SIGTSTP to the foreground process group. This process group contains the editor, as well as any child processes created by it.
The editor has now been stopped. This fact is reported to the session leader using a SIGCHLD signal, which includes the id of the suspended process. When all processes in the foreground job have been suspended, the session leader reads the current configuration from the TTY device, and stores it for later retrieval. The session leader goes on to install itself as the current foreground process group for the TTY using an ioctl call. Then, it prints something like "[1]+ Stopped" to inform the user that a job was just suspended.
The same thing happens if the TTY is connected to a serial port. yes would be able to transmit data at a much higher rate than, say, 9600Â baud, but if the serial port is limited to that speed, the kernel buffer soon fills up and any subsequent write(2) calls block the process (or fail with the error code EAGAIN if the process has requested non-blocking I/O).
What if I told you, that it is possible to explicitly put the TTY in a blocking state even though there is space left in the kernel buffer? That until further notice, every process trying to write(2) to the TTY automatically blocks. What would be the use of such a feature?
Suppose we're talking to some old VT-100 hardware at 9600Â baud. We just sent a complex control sequence asking the terminal to scroll the display. At this point, the terminal gets so bogged down with the scrolling operation, that it's unable to receive new data at the full rate of 9600Â baud. Well, physically, the terminal UART still runs at 9600Â baud, but there won't be enough buffer space in the terminal to keep a backlog of received characters. This is when it would be a good time to put the TTY in a blocking state. But how do we do that from the terminal?
We have already seen that a TTY device may be configured to give certain data bytes a special treatment. In the default configuration, for instance, a received ^C byte won't be handed off to the application through read(2), but will instead cause a SIGINT to be delivered to the foreground job. In a similar way, it is possible to configure the TTY to react on a stop flow byte and a start flow byte. These are typically ^S (ASCII code 19) and ^Q (ASCII code 17) respectively. Old hardware terminals transmit these bytes automatically, and expect the operating system to regulate its flow of data accordingly. This is called flow control, and it's the reason why your xterm sometimes appears to lock up when you accidentally press ^S.
There's an important difference here: Writing to a TTY which is stopped due to flow control, or due to lack of kernel buffer space, will block your process, whereas writing to a TTY from a background job will cause a SIGTTOU to suspend the entire process group. I don't know why the designers of UNIX had to go all the way to invent SIGTTOU and SIGTTIN instead of relying on blocking I/O, but my best guess is that the TTY driver, being in charge of job control, was designed to monitor and manipulate whole jobs; never the individual processes within them.
To find out what the controlling TTY for your shell is called, you could refer to the ps l listing as described earlier, or you could simply run the tty(1) command.
Some of these settings refer to UART parameters, some affect the line discipline and some are for job control. All mixed up in a bucket for monsieur. Let's have a look at the first line:
Occasionally, you may come across a UNIX system where the backspace key doesn't work. This happens when the terminal emulator transmits a backspace code (either ASCIIÂ 8 or ASCIIÂ 127) which doesn't match the erase setting in the TTY device. To remedy the problem, one usually types stty erase ^H (for ASCIIÂ 8) or stty erase ^? (for ASCIIÂ 127). But please remember that many terminal applications use readline, which puts the line discipline in raw mode. Those applications aren't affected.
As you type, your terminal emulator transmits information to the kernel. Usually, the kernel echoes the same information back to the terminal emulator, allowing you to see what you type. Without character echoing, you can't see what you type, but we're in cooked mode so the line editing facilities are still working. Once you press enter, the line discipline will transmit the edit buffer to cat, which will reveal what your wrote.
You will get your prompt back, but after five seconds, the background job transmits hello, world to the terminal, in the middle of whatever you were typing.
I hope this article has provided you with enough information to get to terms with TTY drivers and line disciplines, and how they are related to terminals, line editing and job control. Further details can be found in the various man pages I've mentioned, as well as in the glibc manual (info libc, "Job Control").
Finally, while I don't have enough time to answer all the questions I get, I do welcome feedback on this and other pages on the site. Thanks for reading!
Disclaimer: I am not responsible for what people (other than myself) write in the forums. Please report any abuse, such as insults, slander, spam and illegal material, and I will take appropriate actions. Don't feed the trolls.
Jag tar inget ansvar för det som skrivs i forumet, förutom mina egna inlÀgg. VÀnligen rapportera alla inlÀgg som bryter mot reglerna, sÄ ska jag se vad jag kan göra. Som regelbrott rÀknas till exempel förolÀmpningar, förtal, spam och olagligt material. Mata inte trÄlarna.
Anonymous
Sun 24-Aug-2008 21:36
Very good and informative article for a complex topic
Only a small correction:
Your statement "icanon switches between raw and cooked mode" is not completely true.
'stty -icanon' still interprets control characters such as Ctrl-C whereas 'stty raw' disables even this and is the real raw mode.
Greetings,
-Andreas.
lft
Linus Ă kesson
Fri 29-Aug-2008 16:42
Very good and informative article for a complex topic
Only a small correction:
Your statement "icanon switches between raw and cooked mode" is not completely true.
'stty -icanon' still interprets control characters such as Ctrl-C whereas 'stty raw' disables even this and is the real raw mode.
Thanks!
Yes, you're quite right. I've fixed it.
Anonymous
Wed 26-Nov-2008 07:13
I have been trying to chew through documents in every unix book possible to explain how the TTY system really works. Your article has been more informative than all of these books. Without a programmers knowledge of how a unix kernel works, it's quite difficult for a System Administrator to understand concepts that are so crucial to their jobs. Thank you for your writing such a great article. Your help is truly appreciated.
Anonymous
Wed 10-Dec-2008 12:27
I really admire you!! I'm not use to post, but this article was by far the best I ever read about tty, i read many articles and even books but nothing so clear like this.
Keep doing things like this please...
Excelente!
Muchas Gracias
Fede Tula
Anonymous
Sat 20-Dec-2008 05:20
Good article. A few years ago, after some experimentation, I wrote up some TTY stuff for myself, because I couldn't find any good documentation. For example, I never understood why process group leaders (and consequently also session leaders) are prevented from calling setsid(2). (See the manual for what setsid(2) does.) I think I can explain it now.
The process P is a pg leader if P.PID = P.PGID. In the example of the article, "Job" means process group, and ls (103) is a process group leader:
ls.PID = 103
ls.PGID = 103
ls.SID = 101
ls.CTTY = /dev/pts/0
Suppose we allow ls to call setsid(2). This would have the following consequences:
ls.PID = 103 # unchanged
ls.PGID = 103 # set to ls.PID, but in fact this is no change!
ls.SID = 103 # set to ls.PID
ls.CTTY =
Now ls is session leader (ls.SID = ls.PID), and ls is process group leader (ls.PGID = ls.PID).
At this point, however, sort (104) would belong to a process group (103) whose leader's (ls's) SID (103) doesn't match sort's SID (101)!
sort.PID = 104
sort.PGID = 103
sort.SID = 101
the pg leader for pg 103 is ls (103):
ls.PID = ls.PGID = 103 = sort.PGID
however
ls.SID = 103 != 101 = sort.SID
We have two processes in the same process group belonging to different sessions!
ls is prevented from calling setsid() because as current process leader its PGID doesn't change when it is set to its PID, while its SID changes. Thus it would leave the session while staying in the process group.
Sort, OTOH, can call setsid(), becuase it also leaves the process group:
sort.PID=104
sort.PGID=104 # leaves process group too
sort.SID=104
sort.CTTY=
fork(2)-ing and calling setsid(2) in the child helps, because the child gets a new PID, which will be different from any PGID of the parent (as that PGID was the PID of some process), and so when the child calls setsid(2), the child.PGID := child.PID operation will actually change the child's inherited PGID and so the child will be able to leave the process group.
Right after fork():
parent.PGID = some_old_PID
child.PID = new_PID
child.PGID = parent.PGID = some_old_PID
The child calls setsid():
child.PGID = child.PID = new_PID != some_old_PID = parent.PGID
A session leader *could* call setsid(), despite also being a process group leader, since neither its PGID nor its SID would change. However, its CTTY would be set to , and this would result in a situation where the original controlling process (= a session leader with a CTTY), for example, your shell, has no more access to the terminal!
Furthermore, there is the rule that when a controlling process dies, each session member (each process P with P.SID = SL.SID) loses access to the terminal (and possibly get a SIGHUP on the next read/write). This clearly shows the intent that no session member shall have access to the terminal when the session leader has none. This principle would be violated if the current session leader could detach from the CTTY by calling setsid(). (Or all session members would have to lose access to the CTTY, just as if the session leader died.)
Anonymous
Thu 25-Dec-2008 19:19
good job & best introductory for TTY
Anonymous
Mon 20-Apr-2009 08:28
amazing man , this is amazing !!! best tty article ever
Anonymous
Tue 2-Jun-2009 19:27
very good article!
Anonymous
Mon 15-Jun-2009 16:10
Holy Smokes dude, those were the days!
Riff
www.absolute-anonymity.us.tc
Anonymous
Mon 15-Jun-2009 19:59
I rarely comment on Blogs as I usually feel my input would not be necessary, but I must say this has been one of the best written and descriptive documents I have had the pleasure of learning from
Anonymous
Mon 15-Jun-2009 22:41
ZZZZZZZZZZZZZZZZZZZZZZZZZZ to long, jesus christ write up a summery for us lazy people
Anonymous
Tue 16-Jun-2009 00:22
I've been looking for an article like this for a very long time... thanks very much for putting this together!
Anonymous
Tue 16-Jun-2009 02:38
Great summary, and very useful. Thanks!
Anonymous
Tue 16-Jun-2009 06:57
Just freaking great!
script to make commands like less (more) adapt to changed screen size.
It even tries to exit from the command leaving the cursor on the "correct" place.
The kludge should work well with anything, that ought to be updated because of
a change in terminal window size.
CAVEATS
It's written for Unix under Mac OsX, doesn't really know if tput are implemented under Linux.
Well here we go. I'm sorry for the loss of tabs, it should have been indented.
I have used this for a year and a half and it really works.
export LESS=" -I -r -f -J -S -g -M -x 4"
ORIGLINES=$LINES
ESC=printf "\e"
ScreenRedraw_off=echo -n "$ESC""[8m"
ScreenRedraw_on=echo -n "$ESC""[0m"
function OkayScreen()
{
export PS1="" # Turns off the prompt to avoid cluttering..
echo -n ${ScreenRedraw_off}
CURLINES=bash -i < ~/bin/kludge.bash
if [ $CURLINES -gt $ORIGLINES ] ; then
TO_SKIP="$(expr "$CURLINES" '-' "$ORIGLINES")"
if [ $TO_SKIP -lt 3 ] ; then
TO_SKIP="$(expr "$TO_SKIP" '-' '2')"
else
TO_SKIP="$(expr "$TO_SKIP" '-' '1')"
fi
tput cuu 1 #cursor up one line
echo -n ${ScreenRedraw_on}
echo -n "$" #restores prompt
echo -n ${ScreenRedraw_off}
tput cud $TO_SKIP
echo -n ${ScreenRedraw_on}
echo # activate cli correct position.
else
tput cuu 2
echo ${ScreenRedraw_on}
fi
}
trap OkayScreen SIGWINCH
/usr/bin/less $@
trap '' SIGWINCH
Anonymous
Tue 16-Jun-2009 07:05
Just freaking great!
Forgot the innerpart, which makes it all work ....
This is a second script called kludge.bash which I have in my ~/bin folder.
Needs to execute this to get the changed winsize in a new process since at
least bash 2.05a didn't update the LINE variable in active process in the terminal window.
PS1=""
shopt -s checkwinsize
echo $LINES
Anonymous
Tue 16-Jun-2009 23:58
Thanks for the great article!
I'd recommend adding some info about the *wide* spread myth of parent's death triggering SIGHUPs for all its children.
Recall that the related behavior only applies to session leaders, and is triggered in any of the two following cases:
IF session leader exiting
..IF it has ctty
....send SIGHUP to foreground PG
..ELSE
....send SIGHUP to foreground PG at last time it had ctty
IF session leader detaching (TIOCNOTTY)
..send SIGHUP to foreground PG
--JuanJo
Anonymous
Wed 17-Jun-2009 10:42
Actually, to be more precise:
IF session leader exiting
..IF it has ctty
....send SIGHUP to foreground PG
..ELSE
....send SIGHUP,SIGCONT to foreground PG at last time it had ctty
IF session leader detaching (TIOCNOTTY)
..send SIGHUP,SIGCONT to foreground PG
See: http://google.com/codesearch/p?hl=en&sa=N&cd=2&ct=rc#p4tPAkVsQ%5C_c/linux-2.2.26/drivers/char/tty%5C_io.c&l=537"
--JuanJo AKA jjo
lft
Linus Ă kesson
Sun 21-Jun-2009 12:58
A reader pointed out that VT (in VT-100) stands for "video terminal", not "virtual terminal". This has been fixed.
Anonymous
Thu 30-Jul-2009 13:07
Thanks for the article, really helped !
Anonymous
Thu 30-Jul-2009 19:09
Thankyou, I found this to be an excellent TTY primer.
Anonymous
Fri 31-Jul-2009 22:14
Bookmarked.
Good tips =)
Anonymous
Mon 10-Aug-2009 07:21
Very nice. I'm currently (trying to) write a Unix-clone mostly from scratch, and this is a good resource on how TTYs are supposed to work.
Anonymous
Sun 16-Aug-2009 15:32
Hi--how are things in Sweden?
Every once in a while I get up the ambition to complain about the width of text on a web page, and you're the lucky winner today--sorry ;-)
This could be a good article--from the looks of it, it probably is--but why is it (and so many other web pages today) so wide?
Checking one line at random, it is 130 characters wide:
echo "Meanwhile, however, the computers â still quite large and primitive, but able to multitask â were becoming powerful enough to" | wc
1 20 130
Oh, and I'm ignoring the stuff in the left hand panel / column--I simply horizontally scroll so that panel is not visible.
I have three choices if I want to read your article:
Ideally, and I've seen it done this way, so I believe it can be done:
Anyway, sorry for the rant--thanks for making the effort to create and disseminate pages with information like this!
Randy Kramer
Anonymous
Fri 4-Sep-2009 09:24
Wonderful article.the explanation is meticulous and elegant. Thank you.
Anonymous
Sat 12-Sep-2009 14:09
GREAT ARTICLE!!! Read articles and books on Terminal I/O and none as clear as this summary.
Well Done!
Anonymous
Wed 23-Sep-2009 14:26
Thanks for a good article
Anonymous
Sat 17-Oct-2009 08:12
Thanks for this very good article
Anonymous
Tue 3-Nov-2009 17:53
Thanks for such a wonderful article. I am a beginner in tty and your article was of great help!!
Anonymous
Thu 5-Nov-2009 19:54
nice article..thanx for sharing ur knowledge :)
Anonymous
Tue 22-Dec-2009 20:58
Thanks a lot for this precise article ! This has been very useful to me... (reading an external device on RS232 from bash...)
Anonymous
Tue 9-Feb-2010 07:34
Thank you for sharing such a great artical. -Hai
Anonymous
Thu 15-Apr-2010 09:08
Thanks a lot for this great article
Anonymous
Tue 4-May-2010 08:36
Wonderful article! I never learned much about unix process stuff (apart from little practical things like piping, detaching, killing, etc), but now I feel like I'm starting to see what's behind the magical terminals! Thank you :)
Anonymous
Wed 12-May-2010 01:00
"daemonizing" a process - detaching from the tty - would probably also fit here. -rurban
Anonymous
Thu 13-May-2010 01:17
What does TTY mean?
lft
Linus Ă kesson
Thu 13-May-2010 15:33
What does TTY mean?
TeleTYpe.
Anonymous
Wed 14-Jul-2010 03:21
What Randy Kramer said.
Anonymous
Sat 24-Jul-2010 14:28
excellent article, well done!
Anonymous
Sun 25-Jul-2010 00:31
The process of daemonizing is covered elsewhere, easy to Google. It involved forking, killing the parent, then calling setsid() in the child, and optionally chdir()ing to / and closing stdin/stdout/stderr.
I am currently working on writing a toy OS, and this was very useful in its treatment of the basic structure of the TTY subsystem. Thanks.
ralph
Ralph Corderoy
Sun 25-Jul-2010 14:22
Nice article, various points...
The erase and kill characters used to be # and @, and as you were printing on paper there was no rubbing out, so you might see
$ ls @wc -l /etvc##c/passwd
42 /etc/passwd
$
where the @' was killing the whole line entered so far and the##' was erasing the preceding `vc'.
It's only modern shell that provide line editing, hence shell history substitutions like !!' and!