91

I am trying to figure out how a tty works1 (the workflow and responsibilities of each element). I have read several interesting articles about it, but there are still some blurry areas.

This is what I understand so far:

  • The emulated terminal makes different system calls to /dev/ptmx, the master part of the pseudo terminal.
  • The master part of the pseudo terminal allocates a file in /dev/pts/[0-N], corresponding to the obsolete serial port, and "attaches" a slave pseudo terminal to it.
  • The slave pseudo terminal keeps information such as session ID, foreground job, screen size.

Here are my questions:

  1. Has ptmx any purpose besides allocating the slave part? Does it provide some kind of "intelligence", or does the emulated terminal (xterm for instance) have all the intelligence of behaving like a terminal?
  2. Why does xterm have to interact with the master part, as it only forwards the stdout and stdin of the slave part? Why can't it directly write and read from the pts file?
  3. Is a session ID always attached to one pts file and vice versa? Could I execute ps and find two session IDs for the same /dev/pts/X?
  4. What other information does the pts store? Does xterm update all fields by itself, or does the ptm add some "intelligence" to it?

1. I base my understanding on the TTY demystified by Linus Åkesson, and the Linux Kernel by Andries Brouwer posts, as on several other questions on these sites

Pierre-Jean
  • 2,239

4 Answers4

87

Terminal emulators

The master side replaces the line (the pair of TX/RX wires) that goes to the terminal.

The terminal displays the characters that it receives on one of the wires (some of those are control characters and make it do things like move the cursor, change colour...) and sends on another wire the characters corresponding to the keys you type.

Terminal emulators like xterm are not different except that instead of sending and receiving characters on wires, they read and write characters on their file descriptor to the master side. Once they've spawned the slave terminal, and started your shell on that, they no longer touch that. In addition to emulating the pair of wire, xterm can also change some of the line discipline properties via that file descriptor to the master side. For instance, they can update the size attributes so a SIGWINCH is sent to the applications that interact with the slave pty to notify them of a changed size.

Other than that, there is little intelligence in the terminal/terminal emulator.

What you write to a terminal device (like the pty slave) is what you mean to be displayed there, what you read from it is what you have typed there, so it does not make sense for the terminal emulator to read or write to that. They are the ones at the other end.


The tty line discipline

A lot of the intelligence is in the tty line discipline. The line discipline is a software module (residing in the driver, in the kernel) pushed on top of a serial/pty device that sits between that device and the line/wire (the master side for a pty).

A serial line can have a terminal at the other end, but also a mouse or another computer for networking. You can attach a SLIP line discipline for instance to get a network interface on top of a serial device (or pty device), or you can have a tty line discipline. The tty line discipline is the default line discipline at least on Linux for serial and pty devices. On Linux, you can change the line discipline with ldattach.

You can see the effect of disabling the tty line discipline by issuing stty raw -echo (note that the bash prompt or other interactive applications like vi set the terminal in the exact mode they need, so you want to use a dumb application like cat to experiment with that). Then, everything that is written to the slave terminal device makes it immediately to the master side for xterm to read, and every character written by xterm to the master side is immediately available for reading from the slave device.

The line discipline is where the terminal device internal line editor is implemented. For instance with stty icanon echo (as is the default), when you type a, xterm writes a to the master, then the line discipline echoes it back (makes a a available for reading by xterm for display), but does not make anything available for reading on the slave side. Then if you type backspace, xterm sends a ^? or ^H character, the line discipline (as that ^? or ^H corresponds to the erase line discipline setting) sends back on the master a ^H, space and ^H for xterm to erase the a you've just typed on its screen and still doesn't send anything to the application reading from the slave side, it just updates its internal line editor buffer to remove that a you've typed before.

Then when you press Enter, xterm sends ^M (CR), which the line discipline converts on input to a ^J (LF), and sends what you've entered so far for reading on the slave side (an application reading on /dev/pts/x will receive what you've typed including the LF, but not the a since you've deleted it), while on the master side, it sends a CR and LF to move the cursor to the next line and the start of the screen.

The line discipline is also responsible for sending the SIGINT signal to the foreground process group of the terminal when it receives a ^C character on the master side etc.

Many interactive terminal applications disable most of the features of that line discipline to implement it themselves. But in any case, beware that the terminal (xterm) has little involvement in that (except displaying what it's told to display).

And there can be only one session per process and per terminal device. A session can have a controlling terminal attached to it but does not have to (all sessions start without a terminal until they open one). xterm, in the process that it forks to execute your shell will typically create a new session (and therefore detach from the terminal where you launched xterm from if any), open the new /dev/pts/x it has spawned, by that attaching that terminal device to the new session. It will then execute your shell in that process, so your shell will become the session leader. Your shell or any interactive shell in that session will typically juggle with process groups and tcsetpgrp(), to set the foreground and background jobs for that terminal.

As to what information is stored by a terminal device with a tty discipline (serial or pty), that's typically what the stty command displays and modifies. All the discipline configuration: terminal screen size, local, input output flags, settings for special characters (like ^C, ^Z...), input and output speed (not relevant for ptys). That corresponds to the tcgetattr()/tcsetattr() functions which on Linux map to the TCGETS/TCSETS ioctls, and TIOCGWINSZ/TIOCSWINSZ for the screen size. You may argue that the current foreground process group is another information stored in the terminal device (tcsetpgrp()/tcgetpgrp(), TIOC{G,S}PGRP ioctls), or the current input or output buffer.

Note that that screen size information stored in the terminal device may not reflect reality. The terminal emulator will typically set it (via the same ioctl on the master size) when its window is resized, but it can get out of sync if an application calls the ioctl on the slave side or when the resize is not transmitted (in case of an ssh connection which implies another pty spawned by sshd if ssh ignores the SIGWINCH for instance). Some terminals can also be queried for their size via escape sequences, so an application can query it that way, and update the line discipline with that information.

For more details, you can have a look at the termios and tty_ioctl man pages on Debian for instance.

To play with other line disciplines:

  1. Emulate a mouse with a pseudo-terminal:

    socat pty,link=mouse fifo:fifo
    sudo inputattach -msc mouse # sets the MOUSE line discipline and specifies protocol
    xinput list # see the new mouse there
    exec 3<> fifo
    printf '\207\12\0' >&3 # moves the cursor 10 pixels to the right
    

Above, the master side of the pty is terminated by socat onto a named pipe (fifo). We connect that fifo to a process (the shell) that writes 0x87 0x0a 0x00 which in the mouse systems protocol means no button pressed, delta(x,y) = (10,0). Here, we (the shell) are not emulating a terminal, but a mouse, the 3 bytes we send are not to be read (potentially transformed) by an application from the terminal device (mouse above which is a symlink made by socat to some /dev/pts/x device), but are to be interpreted as a mouse input event.

  1. Create a SLIP interface:

    # on hostA
    socat tcp-listen:12345,reuseaddr pty,link=interface
    # after connection from hostB:
    sudo ldattach SLIP interface
    ifconfig -a # see the new interface there
    sudo ifconfig sl0 192.168.123.1/24
    

    on hostB

    socat -v -x pty,link=interface tcp:hostA:12345 sudo ldattach SLIP interface sudo ifconfig sl0 192.168.123.2/24 ping 192.168.123.1 # see the packets on socat output

Above, the serial wire is emulated by socat as a TCP socket in-between hostA and hostB. The SLIP line discipline interprets those bytes exchanged over that virtual line as SLIP encapsulated IP packets for delivery on the sl0 interface.

  • 1
    Although your answer is well beyond satisfactory, it would be interesting to see a simpler example on how you actually create a /dev/pts/M. I tried using cat /dev/ptmx & which opens a new pty, but then there is no process I can find associated with it,so how would you use it? Second I tried with echo "1" >/dev/ptmx, but that did nothing... Why am I interested in this? Beacause often when one connects remotely via ssh (for example), you get PTY allocation request failed or No controlling tty: open /dev/tty error, which prevents job control. It would be nice to better understand those. – not2qubit Jul 02 '14 at 20:32
  • @user1147688, how to create a pty would be a different question. This one is already too many questions at a time. But see your pty man page for details. – Stéphane Chazelas Jul 03 '14 at 07:09
  • @StéphaneChazelas Small clarifications: 1. So you're saying that the flow is like physical term----tty----bash on terminals, and pty(m)----tty----pty(s)----bash on terminal emulators? Was the tty discipline responsible for echoing characters on physical terminal too? 2. Is it the terminal emulator program which connects to keyboard/screen to manage input? 3. According to what I understood, you said that the line buffering of bash commands/all terminal input is done by tty line discipline instead of I/O buffers of C I/O functions. Is this correct? – forumulator Dec 30 '17 at 16:19
  • "Once they've spawned the slave terminal" yes exactly, apart from that whats the difference? Very elegant how you sneak in the spawning (and then the switching) as a minor thing to get done with. And this wire thing! Now I know you are not S.C., he would explain in kernel functions and not tell kid's stories. –  Oct 13 '19 at 12:13
  • 1
    Is it true the reason a terminal emulator communicates with the master side instead of directly with a line discipline boils down to the well-known interface (the character file) provided by the master pty? – Ilya Loskutov Aug 20 '21 at 18:20
  • Is there a difference between how tty operates on master vs slave sides? To my knowledge, if we have stty echo set the line discipline would send echo for the master side only. If we have stty isig on then ^C as input from the master side would cause SIGINT to be sent to the slave side, but not vice versa. So does it all mean a program should be somewhat aware of the side it is standing on? – Ilya Loskutov Jan 30 '23 at 13:55
  • @IlyaLoskutov programs other than terminal emulators (or things like expect/script/sshd) sit on the slave side, there's no reason they'd be exposed to the master side. They would have to have created the pair first. – Stéphane Chazelas Jan 30 '23 at 14:39
48

Edit: Since this answer, I wrote a dedicated article on my blog, for people who would be interested on more details.


After a lot of reading, this is what I understood.

  • Has ptmx any purpose besides allocating the slave part? Does it provide some kind of "intelligence", or the emulated terminal (xterm for instance) has all the intelligence of behaving like a terminal?

    /dev/ptmx doesn't allocate the slave part: it allocates the "pseudo terminal master part". /dev/ptmx is not the master pseudo terminal: it is a pseudo terminal master multiplexer. It has been created with the Unix98 PTY standard to avoid race conditions when allocating master pseudo terminal (source).

    The master part (ptm) of the pseudo terminal is not represented in the file system. It is represented by a file descriptor.

    The slave part (pts) is represented by a file in /dev/pts/N where N is a number.

    The pts is obtained from the ptm through the successive call of grandpt, unlockpt, and ptsname (source).

    The ptm replaces the AUR driver dedicated to communicating with the device, and the line edition. So it doesn't emulate in any way a terminal but provides the feature of line edition, and provides a way to visualize and communicate with pts.

    Here is a graph of what was a tty connected to a hardware device TTY communication with AUR

    And here is a graph of an tty connected to a ptm TTY communication with PTM

    The ptm file handles different Ioctl arguments (ISPTM, UNLKPT, TIOCREMOTE, TIOCSIGNAL) than the pts.

  • Why does xterm has to interact with the master part, as it only forwards the stdout and stdin of the slave part? Why can't it directly write and read from the pts file?

    Processes interact with devices through actions done to a virtual file (read, write, ioctl..). The file itself doesn't exist and the driver uses the file to trigger actions when read or write methods are called. (See annex for information about drivers)

    A TTY defines a precise way to interact with it. Processes write and read from the device and expect the same behavior regardless of what kind of TTY is implemented.

    • read function is used by processes to read entries from the terminal
    • write function is used by processes to send output to the terminal

    The pts behave like a TTY driver. Its read and write methods are used to implements the TTY driver behaviour. As there is no real device to send the data, a stream pair is created and the ptm implements a read function to read the data that are sent by pts to the stream, and a write function to send data to the stream that will be available when the pts will read it.

    Remember, the file representing a device is not a classic file, and if xterm wants to see what has been written to the file, it cannot just simply call open and read it, as these functions have a completely different behavior here.

  • Has a session ID always attached to one pts file and vice versa? Could I type a ps command and found 2 sessionId for the same /dev/pts/X?

    I don't think so, the session Id is defined by the first process that attach the pts (bash generally), and I don't see a way to create another session and attach it to the same pts. Maybe a tool like socat could do this?

  • What other information does the pts store? Does Xterm update all fields by himself, or does the ptm add some "intelligence" on it?

    The pts stores two categories of information regarding the terminal it is communicating with: the Terminfo and the Termcap. Usually, many terminal emulators are based on a library that manage termcap information for them (that will provide all capabilities values to emulate a VTX100 for instance). An example of such a library is libvte. Edit (seeStephane Chazelas comment): The terminal capabilities are not stored by the pts.

Annex

Pierre-Jean
  • 2,239
  • 1
    termcap and terminfo are databases about terminal or terminal emulator capabilities, they have nothing to do with tty or pty devices. – Stéphane Chazelas Mar 17 '14 at 14:48
  • 1
    Ok, I'll edit my answer. Thanks for the comment. Can you add this information about pts on your answer if you know it(apparently pts does store the screen size, for instance)? – Pierre-Jean Mar 17 '14 at 15:03
  • 7
    These are nice images. What software did you use to make them? – Gilles 'SO- stop being evil' Sep 29 '14 at 23:18
  • 6
    @Gilles Thank you. I did them with Inkscape, an open-source vector graphics editor. It's maybe not the most efficient way to do this kind of graphics, but if you're interested, I wrote an article on how to create this kind of isometric drawings. – Pierre-Jean Oct 01 '14 at 12:09
  • I Don't think you can ever attch two sessions to a controlling terminal or let one session have more than one controlling terminal – 炸鱼薯条德里克 Jun 23 '19 at 01:20
  • Is it true the reason a terminal emulator communicates with the master side instead of directly with a line discipline boils down to the well-known interface (the character file) provided by the master pty? – Ilya Loskutov Aug 20 '21 at 18:20
  • 2
    Neat looking images, but very hard to read because of the intense skew. Thanks for a thoughtful answer. – BEVR1337 May 26 '22 at 23:14
24

Here is a scheme I made some time ago about how sshd works. It doesn't concern the operation of line discipline and stuff, but it adds a real-life illustration of who interacts with what:

enter image description here

Boris Burkov
  • 3,980
  • thank you so much for this. I spent 2 days trying to figure it out. I'm just wondering what happens when no pty is instantiated. stdin does not exists, fine, but where are stdout and stderr written to? – little-dude Jul 31 '15 at 16:49
  • @emmasculateur glad it helped you. Sorry, I can't understand what you mean by "when no pty is instantiated". Can you give an example of when pty is not instantiated? – Boris Burkov Jul 31 '15 at 17:51
  • 1
    By "no pty is instantiated" I mean when you run ssh with -T, which the man says it disables pseudo terminal allocation. e.g: ssh -T emasculateur@localhost "sleep 10" then ps aux|grep sleep shows this: emasculateur 21826 0.0 0.0 23032 3728 ? Ss 02:49 0:00 zsh -c sleep 10

    In that case where does bash write stdout and stderr? I hope my question makes sense.

    – little-dude Aug 01 '15 at 09:54
  • @emmasculateur hm, it's a good question, makes sense, I just didn't think of it earlier. I guess that's the way, you start your process as a daemon on remote machine, without associated terminal. My guess is that its standard input/output/error just go to /dev/null like for a normal daemon, but not sure. See also: http://serverfault.com/questions/593399/what-is-the-benefit-of-not-allocating-a-terminal-in-ssh – Boris Burkov Aug 01 '15 at 11:04
  • @emmasculateur I also stumbled upon a different case from yours: if your process used to have terminal, but that terminal was closed, process would receive SIGHUP from kernel upon read/write attempt to stdout/stdin. This often kills the jobs, started via ssh without nohup or screen/tmux. – Boris Burkov Aug 01 '15 at 11:07
  • 1
    I think ssh also reads stdin from slave side pseudoterminal. – JiaHao Xu Oct 01 '20 at 07:07
  • @BorisBurkov I just use /proc/[pid/fd to check where stdin, stdout and stderr goes to for a ssh session. I found out that the ssh shares the same psudo tty as its parent process, so this graph might be wrong. – JiaHao Xu Oct 01 '20 at 08:23
  • @BorisBurkov I also checked meaning of ssh -T in this. Turns out that -T or -t has nothing to do with whether ssh would allocate a pty at all, it controls whether sshd creates a new pty or not. If a new pty is created in sshd, then ssh would forward information about pty to sshd via TCP. – JiaHao Xu Oct 01 '20 at 08:24
  • @BorisBurkov do you have links to references that you used to learn more about how sshd works and draw your awesome diagram? – ricpacca Oct 03 '20 at 00:53
  • @ricpacca Unfortunately, it's been 6 years now. I briefly googled for the links, but couldn't find the ones I used. Sorry. – Boris Burkov Oct 03 '20 at 09:39
0

man pts says:

The file /dev/ptmx is a character file with major number 5 and minor number 2, usually of mode 0666 and owner.group of root.root. It is used to create a pseudo-terminal master and slave pair.

When a process opens /dev/ptmx, it gets a file descriptor for a pseudo- terminal master (PTM), and a pseudo-terminal slave (PTS) device is created in the /dev/pts directory. Each file descriptor obtained by opening /dev/ptmx is an independent PTM with its own associated PTS, whose path can be found by passing the descriptor to ptsname(3).

Before opening the pseudo-terminal slave, you must pass the master’s file descriptor to grantpt(3) and unlockpt(3).

Once both the pseudo-terminal master and slave are open, the slave provides processes with an interface that is identical to that of a real terminal.

Data written to the slave is presented on the master descriptor as input. Data written to the master is presented to the slave as input.

In practice, pseudo-terminals are used for implementing terminal emulators such as xterm(1), in which data read from the pseudo-terminal master is interpreted by the application in the same way a real terminal would interpret the data, and for implementing remote-login programs such as sshd(8), in which data read from the pseudo-terminal master is sent across the network to a client program that is connected to a terminal or terminal emulator.

Pseudo-terminals can also be used to send input to programs that normally refuse to read input from pipes (such as su(8), and passwd(8)).

About /dev/pts/X indexing :

each X is a session you open it , So slaves need to indexing.

About TeteType (/dev/ttyN):

It's real console has been generated by your boot system such as sysV.

About Why slave insted of master: http://commons.wikimedia.org/wiki/File:Termios-script-diagram.png

PersianGulf
  • 10,850
  • 1
    I'm sorry, but you didn't reply to the questions. I already read the man page and see this graph, but the behavior wasn't clear. Can you, as suggests illuminÉ, extends your response according to the questions? – Pierre-Jean Mar 17 '14 at 01:04
  • SORRY FOR LATE – PersianGulf Mar 17 '14 at 02:40
  • To use the pseudo-TTY subsystem, a node for the master side driver /dev/ptmx and N number of slave drivers (N is determined at installation) must be installed. The names of the slave devices are /dev/pts/M where M has the values 0 through N-1. A user accesses a pseudo-TTY device through the master device (called ptm) that in turn is accessed through the clone driver.The master device is set up as a clone device where its major device number is the major for the clone device and its minor device number is the major for the ptm driver. – PersianGulf Mar 17 '14 at 02:40
  • yes i read man pan page....! – PersianGulf Mar 17 '14 at 02:41
  • it's good point : http://h20566.www2.hp.com/portal/site/hpsc/template.BINARYPORTLET/public/kb/docDisplay/resource.process/?javax.portlet.begCacheTok=com.vignette.cachetoken&javax.portlet.endCacheTok=com.vignette.cachetoken&javax.portlet.rid_ba847bafb2a2d782fcbb0710b053ce01=docDisplayResURL&javax.portlet.rst_ba847bafb2a2d782fcbb0710b053ce01=wsrp-resourceState%3DdocId%253Demr_na-c02267722-2%257CdocLocale%253Den_US&javax.portlet.tpst=ba847bafb2a2d782fcbb0710b053ce01_ws_BI&ac.admitted=1395024227209.876444892.199480143 – PersianGulf Mar 17 '14 at 02:45