Lorsque nous tapons une lettre sur le clavier, comment est-elle envoyée au processus correspondant ? Nous voyons des sorties comme tty1 et pts/0 via ps, who et d'autres commandes. Quelles sont leurs fonctions et différences ?
Histoire TTY
Avant l'avènement des ordinateurs prenant en charge le multitâche
Avant l'apparition des ordinateurs, les gens utilisaient déjà un appareil appelé télétype pour se transmettre des informations. Cela ressemblait à ceci :
+----------+ Physical Line +----------+ | teletype |<--------------------->| teletype | +----------+ +----------+
Deux télétypes sont connectés. lignes. Il peut également y avoir des appareils similaires aux modems aux deux extrémités de la ligne (ils sont ignorés ici). Lors de la saisie sur le clavier à une extrémité du télétype, les données correspondantes seront envoyées à l'autre extrémité du télétype. fonction spécifique Ce qu'il fait, je ne sais pas grand-chose. (Dans ma tête, j'imaginais que l'image était tapée à un bout et imprimée à l'autre)
Ce sont de vieilles antiquités et je n'y ai jamais été exposé, je ne peux donc faire que de simples spéculations.
Après l'émergence des ordinateurs prenant en charge le multitâche
Après que les ordinateurs prennent en charge le multitâche, les gens ont pensé à connecter ces télétypes à l'ordinateur comme terminal de l'ordinateur afin de pouvoir faire fonctionner l'ordinateur.
Il y a deux raisons principales d'utiliser le télétype (avis personnel) :
Il existe déjà en réalité un grand nombre de télétypes de différents fabricants, qui peuvent exploiter pleinement les ressources existantes
Le réseau de télétype associé est relativement mature et connecté Pratique
Donc la connexion s'est développée comme ceci :
+----------+ +----------+ +-------+ Physical Line +-------+ +------+ | | | Terminal |<->| Modem |<--------------------->| Modem |<->| UART |<->| Computer | +----------+ +-------+ +-------+ +------+ | | +----------+
Le terminal de gauche est une variété de télétypes
Les modems sont utilisés des deux côtés de la ligne physique, c'est ce que nous appelons souvent « chat », c'est-à-dire que parce que le réseau s'est progressivement développé, tout le monde peut partager la connexion. (Une supposition approximative, peut-être fausse)
UART peut être compris comme un appareil qui convertit les signaux de télétype en signaux pouvant être reconnus par les ordinateurs
Sous-système Kernel TTY
Afin de prendre en charge ces télétypes, l'ordinateur a conçu un nom appelé TTY La structure interne du sous-système est la suivante :
+-----------------------------------------------+ | Kernel | | +--------+ | | +--------+ +------------+ | | | +----------------+ | | UART | | Line | | TTY |<---------->| User process A | <------>| |<->| |<->| | | +----------------+ | | driver | | discipline | | driver |<---------->| User process B | | +--------+ +------------+ | | | +----------------+ | +--------+ | | | +-----------------------------------------------+
Le pilote UART se connecte aux périphériques UART externes
La discipline de ligne effectue principalement un certain traitement des entrées et des sorties. Pilote TTY
Pilote TTY Utilisé pour gérer divers terminaux
Le processus de l'espace utilisateur traite le terminal via le pilote TTY
Par souci de simplicité, le pilote UART et la discipline Line ne seront pas répertoriés séparément dans l'introduction suivante. Ils peuvent être considérés comme faisant partie du pilote TTY
Périphérique TTY
Pour chaque terminal, le pilote TTY créera un périphérique TTY correspondant. Si plusieurs terminaux sont connectés, il ressemblera à cela. ceci :
+----------------+ | TTY Driver | | | | +-------+ | +----------------+ +------------+ | | |<---------->| User process A | | Terminal A |<--------->| ttyS0 | | +----------------+ +------------+ | | |<---------->| User process B | | +-------+ | +----------------+ | | | +-------+ | +----------------+ +------------+ | | |<---------->| User process C | | Terminal B |<--------->| ttyS1 | | +----------------+ +------------+ | | |<---------->| User process D | | +-------+ | +----------------+ | | +----------------+
Lorsque le pilote reçoit Lorsqu'un terminal est connecté, un périphérique tty correspondant sera créé en fonction du modèle et des paramètres du terminal (le nom de l'appareil dans l'image ci-dessus est appelé ttyS0 car la plupart des connexions de terminal sont des connexions série depuis). chaque terminal peut être différent, il a ses propres commandes spéciales et habitudes d'utilisation, donc la configuration de chaque appareil tty peut être différente. Par exemple, lorsque vous appuyez sur la touche Suppr, certaines peuvent supprimer les caractères précédents et d'autres peuvent supprimer les caractères suivants. Si elles ne sont pas configurées correctement, certaines touches peuvent ne pas se comporter comme vous le souhaitez. si la configuration par défaut ne correspond pas à nos habitudes, une configuration personnalisée doit être effectuée.
Plus tard, avec le développement continu des ordinateurs, les appareils de télétype ont progressivement disparu. Nous n'avons plus besoin d'équipement terminal spécialisé. Chaque machine possède son propre clavier et son propre moniteur, chaque machine peut être un terminal pour d'autres machines, à distance. ssh, mais l'architecture du pilote TTY du noyau n'a pas changé. Si nous voulons interagir avec les processus du système pour les E/S, nous devons toujours utiliser des périphériques TTY, donc divers logiciels de simulation de terminal sont apparus, et les simulations sont également plusieurs. terminaux communs, tels que VT100, VT220, XTerm, etc.
Comment les programmes gèrent TTYVous pouvez utiliser la commande infocmp pour comparer la différence entre deux terminaux, tels que infocmp vt100 vt220 affichera la différence entre vt100 et vt220.
Vous pouvez utiliser la commande
toe -a
pour lister tous les types de terminaux pris en charge par le systèmetoe -a
列出系统支持的所有终端类型可以通过命令infocmp来比较两个终端的区别,比如
infocmp vt100 vt220
Avant de discuter de la façon dont les appareils TTY sont créés et configurés, examinons d'abord comment TTY est utilisé par les processus :
#先用tty命令看看当前bash关联到了哪个tty dev@debian:~$ tty /dev/pts/1 #看tty都被哪些进程打开了 dev@debian:~$ lsof /dev/pts/1 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME bash 907 dev 0u CHR 136,1 0t0 4 /dev/pts/1 bash 907 dev 1u CHR 136,1 0t0 4 /dev/pts/1 bash 907 dev 2u CHR 136,1 0t0 4 /dev/pts/1 bash 907 dev 255u CHR 136,1 0t0 4 /dev/pts/1 lsof 1118 dev 0u CHR 136,1 0t0 4 /dev/pts/1 lsof 1118 dev 1u CHR 136,1 0t0 4 /dev/pts/1 lsof 1118 dev 2u CHR 136,1 0t0 4 /dev/pts/1 #往tty里面直接写数据跟写标准输出是一样的效果 dev@dev:~$ echo aaa > /dev/pts/2 aaaCopier après la connexion
pts sont également des appareils TTY, leur relation est derrière tout cela. sera présenté que
Il ressort du lsof ci-dessus que les stdin(0u), stdout(1u) et stderr(2u) des processus bash et lsof en cours d'exécution sont tous liés à ce TTY.- Ce qui suit est un schéma structurel de l'interaction entre tty et les processus et périphériques d'E/S :
🎜🎜Vous pouvez comprendre tty comme un tube, et le contenu écrit à une extrémité peut être lu à partir de l'autre extrémité, et vice-versa. versa. 🎜
Input +--------------------------+ R/W +------+ ----------->| |<---------->| bash | | pts/1 | +------+ <-----------| |<---------->| lsof | Output | Foreground process group | R/W +------+ +--------------------------+
Copier après la connexion 这里input和output可以简单的理解为键盘和显示器,后面会介绍在各种情况下input/ouput都连接的什么东西。
tty里面有一个很重要的属性,叫Foreground process group,记录了当前前端的进程组是哪一个。process group的概念会在下一篇文章中介绍,这里可以简单的认为process group里面只有一个进程。
当pts/1收到input的输入后,会检查当前前端进程组是哪一个,然后将输入放到进程组的leader的输入缓存中,这样相应的leader进程就可以通过read函数得到用户的输入
当前端进程组里面的进程往tty设备上写数据时,tty就会将数据输出到output设备上
当在shell中执行不同的命令时,前端进程组在不断的变化,而这种变化会由shell负责更新到tty设备中
从上面可以看出,进程和tty打交道很简单,只要保证后台进程不要读写tty就可以了,即写后台程序时,要将stdin/stdout/stderr重定向到其它地方(当然deamon程序还需要做很多其它处理)。
先抛出两个问题(后面有答案):
当非前端进程组里面的进程(后台进程)往tty设备上写数据时,会发生什么?会输出到outpu上吗?
当非前端进程组里面的进程(后台进程)从tty设备上读数据时,会发生什么?进程会阻塞吗?
TTY是如何被创建的
下面介绍几种常见的情况下tty设备是如何创建的,以及input和output设备都是啥。
键盘显示器直连(终端)
先看图再说话:
+-----------------------------------------+ | Kernel | | +--------+ | +----------------+ +----------+ | +-------------------+ | tty1 |<---------->| User processes | | Keyboard |--------->| | +--------+ | +----------------+ +----------+ | | Terminal Emulator |<->| tty2 |<---------->| User processes | | Monitor |<---------| | +--------+ | +----------------+ +----------+ | +-------------------+ | tty3 |<---------->| User processes | | +--------+ | +----------------+ | | +-----------------------------------------+
键盘、显示器都和内核中的终端模拟器相连,由模拟器决定创建多少tty,比如你在键盘上输入ctrl+alt+F1时,模拟器首先捕获到该输入,然后激活tty1,这样键盘的输入会转发到tty1,而tty1的输出会转发到显示器,同理用输入ctrl+alt+F2,就会切换到tty2。
当模拟器激活tty时如果发现没有进程与之关联,意味着这是第一次打开该tty,于是会启动配置好的进程并和该tty绑定,一般该进程就是负责login的进程。
当切换到tty2后,tty1里面的输出会输出到哪里呢?tty1的输出还是会输出给模拟器,模拟器里会有每个tty的缓存,不过由于模拟器的缓存空间有限,所以下次切回tty1的时候,只能看到最新的输出,以前的输出已经不在了。
不确定这里的终端模拟器对应内核中具体的哪个模块,但肯定有这么个东西存在
SSH远程访问
+----------+ +------------+ | Keyboard |------>| | +----------+ | Terminal | | Monitor |<------| | +----------+ +------------+ | | ssh protocol | ↓ +------------+ | | | ssh server |--------------------------+ | | fork | +------------+ | | ↑ | | | | write | | read | | | | +-----|---|-------------------+ | | | | | ↓ | ↓ | +-------+ | +-------+ | +--------+ | pts/0 |<---------->| shell | | | | +-------+ | +-------+ | | ptmx |<->| pts/1 |<---------->| shell | | | | +-------+ | +-------+ | +--------+ | pts/2 |<---------->| shell | | +-------+ | +-------+ | Kernel | +-----------------------------+
这里的Terminal可能是任何地方的程序,比如windows上的putty,所以不讨论客户端的Terminal程序是怎么和键盘、显示器交互的。由于Terminal要和ssh服务器打交道,所以肯定要实现ssh的客户端功能。
这里将建立连接和收发数据分两条线路解释,为了描述简洁,这里以sshd代替ssh服务器程序:
建立连接
1.Terminal请求和sshd建立连接
2.如果验证通过,sshd将创建一个新的session
3.调用API(posix_openpt())请求ptmx创建一个pts,创建成功后,sshd将得到和ptmx关联的fd,并将该fd和session关联起来。
#pty(pseudo terminal device)由两部分构成,ptmx是master端,pts是slave端, #进程可以通过调用API请求ptmx创建一个pts,然后将会得到连接到ptmx的读写fd和一个新创建的pts, #ptmx在内部会维护该fd和pts的对应关系,随后往这个fd的读写会被ptmx转发到对应的pts。 #这里可以看到sshd已经打开了/dev/ptmx dev@debian:~$ sudo lsof /dev/ptmx COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME sshd 1191 dev 8u CHR 5,2 0t0 6531 /dev/ptmx sshd 1191 dev 10u CHR 5,2 0t0 6531 /dev/ptmx sshd 1191 dev 11u CHR 5,2 0t0 6531 /dev/ptmx
4.同时sshd创建shell进程,将新创建的pts和shell绑定
收发消息
1.Terminal收到键盘的输入,Terminal通过ssh协议将数据发往sshd
2.sshd收到客户端的数据后,根据它自己管理的session,找到该客户端对应的关联到ptmx上的fd
3.往找到的fd上写入客户端发过来的数据
4.ptmx收到数据后,根据fd找到对应的pts(该对应关系由ptmx自动维护),将数据包转发给对应的pts
5.pts收到数据包后,检查绑定到自己上面的当前前端进程组,将数据包发给该进程组的leader
6.由于pts上只有shell,所以shell的read函数就收到了该数据包
7.shell对收到的数据包进行处理,然后输出处理结果(也可能没有输出)
8.shell通过write函数将结果写入pts
9.pts将结果转发给ptmx
10.ptmx根据pts找到对应的fd,往该fd写入结果
11.sshd收到该fd的结果后,找到对应的session,然后将结果发给对应的客户端
键盘显示器直连(图形界面)
+----------+ +------------+ | Keyboard |------>| | +----------+ | Terminal |--------------------------+ | Monitor |<------| | fork | +----------+ +------------+ | | ↑ | | | | write | | read | | | | +-----|---|-------------------+ | | | | | ↓ | ↓ | +-------+ | +-------+ | +--------+ | pts/0 |<---------->| shell | | | | +-------+ | +-------+ | | ptmx |<->| pts/1 |<---------->| shell | | | | +-------+ | +-------+ | +--------+ | pts/2 |<---------->| shell | | +-------+ | +-------+ | Kernel | +-----------------------------+
为了简化起见,本篇不讨论Linux下图形界面里Terminal程序是怎么和键盘、显示器交互的。
这里和上面的不同点就是,这里的Terminal不需要实现ssh客户端,但需要把ssh服务器要干的活也干了(当然ssh通信相关的除外)。
SSH + Screen/Tmux
常用Linux的同学应该对screen和tmux不陌生,通过它们启动的进程,就算网络断开了,也不会受到影响继续执行,下次连上去时还能看到进程的所有输出,还能继续接着干活。
这里以tmux为例介绍其原理:
+----------+ +------------+ | Keyboard |------>| | +----------+ | Terminal | | Monitor |<------| | +----------+ +------------+ | | ssh protocol | ↓ +------------+ | | | ssh server |--------------------------+ | | fork | +------------+ | | ↑ | | | | write | | read | | | | +-----|---|-------------------+ | | ↓ | | ↓ | +--------+ +-------+ | +-------+ fork +-------------+ | | ptmx |<->| pts/0 |<---------->| shell |-------->| 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 -a speed 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 -iutf8 opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0 isig 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 = ^C
tty除了在终端和前端进程之间转发数据之外,还支持很多控制命令,比如终端输入了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的执行。
Signaux liés au TTY
En plus de SIGINT, SIGTTOU et SIGWINCHU mentionnés dans l'introduction de la configuration ci-dessus, il existe plusieurs signaux liés au TTY
SIGTTIN
Lorsque le processus d'arrière-plan lit le tty, le tty enverra le signal à le groupe de processus correspondant, le comportement par défaut est de suspendre l'exécution des processus dans le groupe de processus. Comment poursuivre l’exécution d’un processus suspendu ? Veuillez vous référer à SIGCONT dans le prochain article.
SIGHUP
Lorsque l'autre extrémité du tty se bloque, par exemple, la session ssh est déconnectée, donc sshd ferme le fd associé à ptmx, le noyau enverra un signal SIGHUP à tous les processus liés au tty, et le processus reçoit Le comportement par défaut après avoir reçu ce signal est de quitter le processus.
SIGTSTP
Lorsque le terminal entre CTRL+Z, le tty enverra SIGTSTP au groupe de processus front-end après l'avoir reçu. Son comportement par défaut est de placer le groupe de processus front-end dans le backend et de suspendre l'exécution de tous. processus dans le groupe de processus.
Les signaux liés au tty peuvent être capturés et son comportement par défaut peut être modifié
Conclusion
Cet article présente les fonctions et fonctionnalités courantes du tty. Le prochain article présentera en détail la session de processus étroitement liée à l'identifiant du tty, au processus. groupe, emploi, programme de formation, etc., restez à l'écoute.
Recommandations associées : "Tutoriel vidéo Linux"