下面由linux系统教程栏目给大家介绍linux tty/pts及其作用区别,希望对需要的朋友有所帮助!
linux tty/pts概述
当我们在键盘上敲下一个字母的时候,到底是怎么发送到相应的进程的呢?我们通过ps、who等命令看到的类似tty1、pts/0这样的输出,它们的作用和区别是什么呢?
tty历史支持多任务的计算机出现之前在计算机出来以前,人们就已经在使用一种叫teletype的设备,用来相互之间传递信息,看起来像下面这样:
+----------+ physical line +----------+| teletype |<--------------------->| teletype |+----------+ +----------+
两个teletype之间用线连接起来,线两端可能也有类似于调制解调器之类的设备(这里将它们忽略),在一端的teletype上敲键盘时,相应的数据会发送到另一端的teletype,具体功能是干什么的,我也不太了解。(我脑袋里面想到画面是在一端敲字,另一端打印出来)
这些都是老古董了,完全没接触过,所以只能简单的推测。
支持多任务的计算机出现之后等到计算机支持多任务后,人们想到把这些teletype连到计算机上,作为计算机的终端,从而可以操作计算机。
使用teletype的主要原因有两个(个人见解):
现实中已经存在了大量不同厂商的teletype,可以充分利用现有资源
teletype的相关网络已经比较成熟,连起来方便
于是连接就发展成这样:
+----------+ +----------+ +-------+ physical line +-------+ +------+ | || terminal |<->| modem |<--------------------->| modem |<->| uart |<->| computer |+----------+ +-------+ +-------+ +------+ | | +----------+
左边的terminal就是各种各样的teletype
物理线路两边用上了modem,就是我们常说的“猫”,那是因为后来网络已经慢慢的变发达了,大家可以共享连接了。(大概推测,可能不对)
uart可以理解为将teletype的信号转换成计算机能识别的信号的设备
内核tty子系统计算机为了支持这些teletype,于是设计了名字叫做tty的子系统,内部结构如下:
+-----------------------------------------------+ | kernel | | +--------+ | | +--------+ +------------+ | | | +----------------+ | | uart | | line | | tty |<---------->| user process a |<------>| |<->| |<->| | | +----------------+ | | driver | | discipline | | driver |<---------->| user process b | | +--------+ +------------+ | | | +----------------+ | +--------+ | | | +-----------------------------------------------+
uart driver对接外面的uart设备
line discipline主要是对输入和输出做一些处理,可以理解它是tty driver的一部分
tty driver用来处理各种终端设备
用户空间的进程通过tty driver来和终端打交道
为了简单起见,后面的介绍中不再单独列出uart driver和line discipline,可以认为它们是tty driver的一部分
tty设备对于每一个终端,tty driver都会创建一个tty设备与它对应,如果有多个终端连接过来,那么看起来就是这个样子的:
+----------------+ | tty driver | | | | +-------+ | +----------------+ +------------+ | | |<---------->| user process a | | terminal a |<--------->| ttys0 | | +----------------+ +------------+ | | |<---------->| user process b | | +-------+ | +----------------+ | | | +-------+ | +----------------+ +------------+ | | |<---------->| user process c | | terminal b |<--------->| ttys1 | | +----------------+ +------------+ | | |<---------->| user process d | | +-------+ | +----------------+ | | +----------------+
当驱动收到一个终端的连接时,就会根据终端的型号和参数创建相应的tty设备(上图中设备名称叫ttys0是因为大部分终端的连接都是串行连接),由于每个终端可能都不一样,有自己的特殊命令和使用习惯,于是每个tty设备的配置可能都不一样。比如按delete键的时候,有些可能是要删前面的字符,而有些可能是删后面的,如果没配置对,就会导致某些按键不是自己想要的行为,这也是我们在使用模拟终端时,如果默认的配置跟我们的习惯不符,需要做一些个性化配置的原因。
后来随着计算机的不断发展,teletype这些设备逐渐消失,我们不再需要专门的终端设备了,每个机器都有自己的键盘和显示器,每台机器都可以是其它机器的终端,远程的操作通过ssh来实现,但是内核tty驱动这一架构没有发生变化,我们想要和系统中的进程进行i/o交互,还是需要通过tty设备,于是出现了各种终端模拟软件,并且模拟的也是常见的几种终端,如vt100、vt220、xterm等。
可以通过命令toe -a列出系统支持的所有终端类型
可以通过命令infocmp来比较两个终端的区别,比如infocmp vt100 vt220将会输出vt100和vt220的区别。
程序如何和tty打交道在讨论tty设备是如何被创建及配置之前,我们先来看看tty是如何被进程使用的:
#先用tty命令看看当前bash关联到了哪个ttydev@debian:~$ tty/dev/pts/1#看tty都被哪些进程打开了dev@debian:~$ lsof /dev/pts/1command pid user fd type device size/off node namebash 907 dev 0u chr 136,1 0t0 4 /dev/pts/1bash 907 dev 1u chr 136,1 0t0 4 /dev/pts/1bash 907 dev 2u chr 136,1 0t0 4 /dev/pts/1bash 907 dev 255u chr 136,1 0t0 4 /dev/pts/1lsof 1118 dev 0u chr 136,1 0t0 4 /dev/pts/1lsof 1118 dev 1u chr 136,1 0t0 4 /dev/pts/1lsof 1118 dev 2u chr 136,1 0t0 4 /dev/pts/1#往tty里面直接写数据跟写标准输出是一样的效果dev@dev:~$ echo aaa > /dev/pts/2aaa
pts也是tty设备,它们的关系后面会介绍到
通过上面的lsof可以看出,当前运行的bash和lsof进程的stdin(0u)、stdout(1u)、stderr(2u)都绑定到了这个tty上。
下面是tty和进程以及i/o设备交互的结构图:
input +--------------------------+ r/w +------+----------->| |<---------->| bash | | pts/1 | +------+| | +--------+ | +----------------+ +----------+ | | terminal emulator |<->| tty2 |<---------->| user processes | | monitor || | +----------+ | terminal | | monitor || | +----------+ | terminal |--------------------------+ | monitor || | +----------+ | terminal | | monitor || tmux client | | +--------+ +-------+ | +-------+ +-------------+ | | | | ↑ | +--------+ +-------+ | +-------+ | | | ptmx |<->| pts/2 |<---------->| shell | | | +--------+ +-------+ | +-------+ | | ↑ | kernel | ↑ | +-----|---|-------------------+ | | | | | | |w/r| +---------------------------+ | | | | fork | | ↓ | | +-------------+ | | | | | tmux server |<--------------------------------------------+ | | +-------------+
系统中的ptmx只有一个,上图中画出来了两个,目的是为了表明tmux服务器和sshd都用ptmx,但它们之间又互不干涉。
这种情况要稍微复杂一点,不过原理都是一样的,前半部分和普通ssh的方式是一样的,只是pts/0关联的前端进程不是shell了,而是变成了tmux客户端,所以ssh客户端发过来的数据包都会被tmux客户端收到,然后由tmux客户端转发给tmux服务器,而tmux服务器干的活和ssh的类似,也是维护一堆的session,为每个session创建一个pts,然后将tmux客户端发过来的数据转发给相应的pts。
由于tmux服务器只和tmux客户端打交道,和sshd没有关系,当终端和sshd的连接断开时,虽然pts/0会被关闭,和它相关的shell和tmux客户端也将被kill掉,但不会影响tmux服务器,当下次再用tmux客户端连上tmux服务器时,看到的还是上次的内容。
tty和pts的区别从上面的流程中应该可以看出来了,对用户空间的程序来说,他们没有区别,都是一样的;从内核里面来看,pts的另一端连接的是ptmx,而tty的另一端连接的是内核的终端模拟器,ptmx和终端模拟器都只是负责维护会话和转发数据包;再看看ptmx和内核终端模拟器的另一端,ptmx的另一端连接的是用户空间的应用程序,如sshd、tmux等,而内核终端模拟器的另一端连接的是具体的硬件,如键盘和显示器。
常见的tty配置先先来看看当前tty的所有配置:
dev@dev:~$ stty -aspeed 38400 baud; rows 51; columns 204; line = 0;intr = ^c; quit = ^\; erase = ^?; kill = ^u; eof = ^d; eol = m-^?; eol2 = m-^?; swtch = <undef>; start = ^q; stop = ^s; susp = ^z; rprnt = ^r; werase = ^w; lnext = ^v; discard = ^o; min = 1; time = 0;-parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc ixany imaxbel -iutf8opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke -flusho -extproc
stty还可以用来修改tty的参数,用法请参考man stty
只要是有权限的程序,都可以通过linux提供的api来修改tty的配置,下面介绍一些常见的的配置项。
rows 51; columns 204;这个配置一般由终端控制,当终端的窗口大小发生变化时,需要通过一定的手段修改该配置,比如ssh协议里面就有修改窗口大小的参数,sshd收到客户端的请求后,会通过api修改tty的这个参数,然后由tty通过信号sigwinch通知前端程序(比如shell或者vim),前端程序收到信号后,再去读tty的这个参数,然后就知道如何调整自己的输出排版了。
intr = ^ctty除了在终端和前端进程之间转发数据之外,还支持很多控制命令,比如终端输入了ctrl+c,那么tty不会将该输入串转发给前端进程,而是将它转换成信号sigint发送给前端进程。这个就是用来配置控制命令对应的输入组合的,比如我们可以配置“intr = ^e”表示用ctrl+e代替ctrl+c。
start = ^q; stop = ^s;这是两个特殊的控制命令,估计经常有人会碰到,在键盘上不小心输入ctrl+s后,终端没反应了,即没输出,也不响应任何输入。这是因为这个命令会告诉tty暂停,阻塞所有读写操作,即不转发任何数据,只有按了ctrl+q后,才会继续。这个功能应该是历史遗留,以前终端和服务器之间没有流量控制功能,所以有可能服务器发送数据过快,导致终端处理不过来,于是需要这样一个命令告诉服务器不要再发了,等终端处理完了后在通知服务器继续。
该命令现在比较常用的一个场景就是用tail -f命令监控日志文件的内容时,可以随时按ctrl+s让屏幕停止刷新,看完后再按ctrl+q让它继续刷,如果不这样的话,需要先ctrl+c退出,看完后在重新运行tail -f命令。
echo在终端输入字符的时候,之所以我们能及时看到我们输入的字符,那是因为tty在收到终端发过去的字符后,会先将字符原路返回一份,然后才交给前端进程处理,这样终端就能及时的显示输入的字符。echo就是用来控制该功能的配置项,如果是-echo的话表示disable echo功能。
-tostop如果你在shell中运行程序的时候,后面添加了&,比如./myapp &,这样myapp这个进程就会在后台运行,但如果这个进程继续往tty上写数据呢?这个参数就用来控制是否将输出转发给终端,也即结果会不会在终端显示,这里“-tostop”表示会输出到终端,如果配置为“tostop”的话,将不输出到终端,并且tty会发送信号sigttou给myapp,该信号的默认行为是将暂停myapp的执行。
tty相关信号除了上面介绍配置时提到的sigint,sigttou,sigwinchu外,还有这么几个跟tty相关的信号
sigttin当后台进程读tty时,tty将发送该信号给相应的进程组,默认行为是暂停进程组中进程的执行。暂停的进程如何继续执行呢?请参考下一篇文章中的sigcont。
sighup当tty的另一端挂掉的时候,比如ssh的session断开了,于是sshd关闭了和ptmx关联的fd,内核将会给和该tty相关的所有进程发送sighup信号,进程收到该信号后的默认行为是退出进程。
sigtstp终端输入ctrl+z时,tty收到后就会发送sigtstp给前端进程组,其默认行为是将前端进程组放到后端,并且暂停进程组里所有进程的执行。
跟tty相关的信号都是可以捕获的,可以修改它的默认行为
结束语本文介绍了常见的tty功能和特点,下一篇中将详细介绍和tty密切相关的进程session id,进程组,job,后台程序等,敬请期待。
相关推荐:《linux视频教程》
以上就是一起聊聊linux tty/pts及其作用区别的详细内容。
