接前一篇文章:Linux内核TTY子系统有什么(5)
本文内容参考:
Linux TTY子系统框架-CSDN博客
一文彻底讲清Linux tty子系统架构及编程实例-CSDN博客
linux TTY子系统(3) - tty driver_sys tty device driver-CSDN博客
Linux TTY 子系统框架_linux tty体系-CSDN博客
LinuxTTY 子系统2_tty子系统-CSDN博客
Linux TTY Driver_tty driver配置-CSDN博客
UART驱动学习二(TTY体系)_serdev-CSDN博客
解密TTY - QiuhaoLi - 博客园
特此致谢!
一、TTY是什么?
2. TTY的分类
Linux系统的终端设备一般分为控制台、伪终端pty、串口终端(/dev/ttySn)和其它类型。
(4)伪终端PTY
上一回阐述了伪终端(Pesudo Terminal,PTY)的基本原理,内容比较简单明了。本回在前一回认知的基础上,讲解更为详细、复杂的内容。
上一回提到,伪终端分为主设备(主终端)和从设备(从终端)。
简单来说,主终端(master)与类似sshd、telnetd等用户空间的远程协议处理进程连接;而从终端(slave)则与shell之类的实际进程连接。在处理远程登录的时候,一般都是由远程协议处理进程打开主终端和从终端,然后就在远程网络终端和本机shell之间建立了一条双向通道:
远程网络终端(套接字) <---> 本机协议处理进程 <---> 主终端 <---> 从终端 <---> shell
在这个“打开主从终端建立连接”的语义以及其实现上,有着不同的标准,总的来说有三种方式:SVR4、BSD、Linux方式。
在“建立连接”的语义上,SVR4的方式使用“流”来建立这条连接,而BSD和Linux则是自动建立的。
可见Linux处理伪终端的方式是结合SVR4和BSD两种UNIX标准的结果。Linux不仅实现这种有意义的最佳组合,而且分别实现了SRV和BSD的两种方式的接口。如果编译CONFIG_LEGACY_PTYS宏,则可以使用BSD的方式;如果编译CONFIG_UNIX98_PTYS,则实现SRV4的接口。
- BSD接口
机制相对简单。master为/dev/pty[p-za-e] [0-9a-f];slave为/dev/tty[p-za-e] [0-9a-f],它们都是配对出现的(例如/dev/ptyp3和/dev/ttyp3)。但由于在编程时要找到一个合适的终端需要逐个尝试,所以逐渐被放弃。
- Unix 98接口(SRV4)
仅使用一个/dev/ptmx作为master设备,任何sshd、telnetd之类的进程都可以只使用这一个终端设备文件,在每次打开操作时会得到一个master设备fd,并在/dev/pts/目录下得到一个slave设备(如/dev/pts/3和/dev/ptmx),这样就避免了逐个尝试的麻烦。在ptmx_open函数中,不仅系统可以自动分配一个主终端,而且还为该主终端绑定了一个从终端,主终端设置到file结构体的private_data字段上,之后诸如sshd、telnetd之类的进程读写/dev/ptmx文件时,虽然它们读写的是同一个文件,可是由于file结构体不在它们之间共享,因此它们取到的file->private_data也就不同了。
由于可能有好几千个用户登录,因此/dev/pts/*是动态生成的,不像其它设备文件那样,是构建系统时就已经产生的硬盘节点(如果未使用devfs、udev、mdev等)。第一个用户登录,设备文件为/dev/pts/0;第二个为/dev/pts/1,依次类推。它们并不与实际物理设备直接相关。现在大多数系统是通过此接口实现pty。
在X Window下打开的终端或使用telnet或ssh等方式登录Linux主机,此时均通过pty设备。例如,如果某人在网上使用telnet程序连接到你的计算机上,则telnet程序就可能会打开/dev/ptmx设备获取一个fd。此时一个getty程序就应该运行在对应的/dev/pts/* 上。当telnet从远端获取了一个字符时,该字符就会通过ptmx、pts/* 传递给getty程序,而getty程序就会通过pts/* 、ptmx和telnet程序往网络上返回“login:”字符串信息。这样,登录程序与telnet程序就通过“伪终端”进行通信。
- telnet<—>/dev/ptmx(master)<—>pts/*(slave)<—>getty
如果一个程序把pts/*看作是一个串行端口设备,则它对该端口的读/写操作会反映在该逻辑终端设备对的另一个/dev/ptmx上,而/dev/ptmx则是另一个程序用于读写操作的逻辑设备。这样,两个程序就可以通过这种逻辑设备进行互相交流,这很象是逻辑设备对之间的管道操作。对于pts/* ,任何设计成使用一个串行端口设备的程序都可以使用该逻辑设备。但对于使用/dev/ptmx的程序,则需要专门设计来使用/dev/ptmx逻辑设备。通过使用适当的软件,就可以把两个甚至多个伪终端设备连接到同一个物理串行端口上。
最后,举一个完整的实例。
- Tmux Server打开一对伪终端(pts/2和ptmx),自己持有主设备(ptmx),将从设备继承给它Fork出来的Bash。此一对进程进入后台,不再归属任何终端。
- 一旦Tmux Client运行于某个SSH终端,它会把当前终端的pts传递给Tmux Server,从而让Tmux Server作为一个数据代理传递输入和输出。
- 一旦Tmux Client运行,它便成为了当前Bash的前台进程,通过重定向之后,当前Tmux Client便成为了Bash0的前端,接收Bash0的输入和输出。文件句柄转交后的流程如下:
1)有人在远端的Windows主机上敲入一个字符“a”;
2)字符“a”经由SSH客户端加密后传输到Linux SSH服务器SSHd并解密;
3)字符“a”通过SSHd的ptmx写入;
4)Tmux Server从pts/2将字符“a”读出并写入ptmx;
5)Bash0将字符“a”从pts/1读出并执行;
6)Bash0将“-bash: a: command not found”按原路返回给Windows。
更多内容请看下回。