站内链接:
终端
名词
在详细讲述下面的知识点之前, 先简单了解下各个终端相关名词
- CLI: 命令行, 图形界面普及之前使用最为广泛的
用户界面
, 用户通过键盘输入指令来告知计算机
- Terminal: 一种用来让用户
输入数据
到计算, 并回显
计算结果的机器, 一个 IO 交互设备
- Console: 一种特殊的终端
- Terminal Emulator: 终端模拟器, 利用程序模拟终端行为, 例如 iterm2
- TTY: 终端的统称, Terminal 等价于 TTY 等价于文本输入,输出环境
- Shell: 命令行解释器, 执行用户输入的命令并返回结果
哑终端和控制台
最早期的大型机和小型机时代, 计算机是庞大的并安置在特殊的房间中, 此时通过某些设备
与计算机进行交互, 这些设备就是最早期的终端
.这些早期的终端一般使用电传打字机(Teletype)
的设备来连接计算机, 从而确保每个用户都能通过终端登录并操作主机, 这是 UNIX 系统创建早期为了解决多用户登录而采取的一种较为廉价的方式. 因为当时计算机设备非常昂贵, 并且键盘和主机是集成在一起的, 没有独立的键盘, 所以使用ASR-33
发送信号给计算机, 并且将结果回传打印到纸袋(注意, 是纸袋哦)上.
![ASR-33 电传打字机](https://image.unusebamboo.top/blog/toolstermasr33.jpeg)
这些终端被称为哑终端
, 是一种相较于其他聪明的终端而言, 不执行删除, 清屏, 控制光标等等操作.
在早期的计算中, 管理员使用一种特殊的终端
来管理主机, 其和计算机主机是一体的, 有着比普通终端更大的权限
, 一台计算机只能有一个控制台, 但是有多个终端, 此时终端和控制台还是两个不同的物体.
下文就是一台console
和终端
, 左边为 console, 右边为 terminal:
![console和terminal](https://image.unusebamboo.top/blog/toolsconsole.jpeg)
但是, 时代在进步, 随着个人计算机的普及, console 和 terminal 的概念逐渐模糊. 在现在看来, 键盘
和显示器既可以认为是console
, 也可以认为是普通终端
.
- 管理系统时: 键盘和显示器就是控制台
- 浏览网页,编辑等时: 键盘和显示器就是终端
两者现在是一个统一的概念.
基于字符和图形终端
终端设备一直在不断的发展, 根据历史发展我们总结终端:
- 字符终端: 文本终端, 仅仅接收和显示
文本信息
的终端, 分为哑终端(dump)和智能终端(intelligent)
- 基于字符的图形终端: 不仅仅可以显示文本信息, 还可以显示图形和图像, 进行复杂的操作
- 运行窗口系统的图形终端: 现在我们使用的窗口系统
下面就是一个较为古老的字符终端设备:VT100, 其是第一批支持 ANSI 转移序列和光标控制的智能终端
![DEC VT100](https://image.unusebamboo.top/blog/toolstermcharacter.jpeg)
终端模拟器
那么, 我现在正在使用的 iterm2, 以及网络上常见的各种终端应用是属于哪一类终端呢?
上面所述的终端都是硬件外设终端, 但是现在都被键盘
和显示器
替代了, 那么开发者如何使用命令行
和计算机交互呢?
为了解决这些问题, 终端模拟器
(Terminal Emulator), 一种模拟传统终端
行为的程序被开发出来, 终端
模拟器在整个计算机交互中担当的老角色:
- CLI 程序: 终端模拟器
假装
成传统的终端设备
- 图形窗口: 终端模拟器
假装
成 GUI 程序
一个终端模拟器的工作流程如下, 实际上充当代理的角色:
- 捕捉键盘输入
- 将键盘输入发送给命令行程序
- 获取命令行输出结果(STDOUT/STDERR)
- 调用图形接口, 比如 X11, 将输出回显到显示器上
虚拟控制台
当你登录 LINUX 图形界面系统之后, 可以按下组合键CTRL + ALT + F1~F6
来切换到 6 个类似原始终端的
全屏界面, 实际上这些也是终端模拟器
的一种, 它被称为虚拟控制台
.
如果你使用云服务器, 当你使用网络终端
登录到远程服务器时, 你可以看到tty1~tty6
六个父进程为 init
的终端进程, 这些就是虚拟控制台
进程.
- 虚拟控制台: 有操作系统内核直接提供的终端界面
- 终端窗口: 运行在图形界面上的终端界面
这两者都是终端模拟器, 他们的原理是类似的.
tty 和 pts
注意, 本章节的内容大多引用同一篇文章:Linux TTY/PTS 概述因为作者实在写的太好了, 我这边就直接抄了好些内容.
tty 演变和使用
1.首先, 在多任务的计算机
出现之前, 人们就已经使用 Teletype 来进行消息传递了, 有点类似点对点功能.
1 2 3
| +----------+ +------+ Physical Line +------+ +----------+ | teletype +----+ modem+---------------------+ modem+---+ teletype | +----------+ +------+ A->B B->A +------+ +----------+
|
2.其次, 在多任务计算机
出现之后, 键盘和显示器等现代高级终端出现之前, 将廉价简易的Teletype
作为
计算机终端以支持多用户登录和操作(计算机大部分技术发展都是这种向后兼容, 基于前者发展).
1 2 3 4 5 6 7 8 9 10 11
| +----------+ +-------+ Physical Line | Terminal +<->+ Modem +<------------------+ +----------+ +-------+ | | +----------+ | +-------+ +------+ | | +---->+ Modem +<->+ UART +<->+ Computer | | +-------+ +------+ | | | +----------+ +----------+ +-------+ Physical Line | | Terminal +<->+ Modem +<------------------+ +----------+ +-------+
|
注意, 这里 Modem 就是以前我们了解的猫
(调制解调器), UART
可以理解为将 teletype 的信号转换成计算
机能识别的信号的设备.
3.最后, 计算机如何支持Teletype
设备? 他们怎样进行通信的呢? 如何查看当前计算支持的 TTY 设备?
计算机为了支持 teletype, 设计了TTY 子系统
, 以便能够外部中断设备进行通信, 其简单框架如下:
1 2 3 4 5 6 7 8 9 10 11
| +-----------------------------------------------+ | Kernel | | +--------+ | +--------+ | +--------+ +------------+ | | | +----------------+ | | | | UART | | Line | | TTY +<---------->+ User process A | |Teletype+------->+ +<->+ +<->+ | | +----------------+ | | | | driver | | discipline | | driver +<---------->+ User process B | +--------+ | +--------+ +------------+ | | | +----------------+ | +--------+ | | | +-----------------------------------------------+
|
这里省略了 Modem 等信息. Line discipline
对输入和输出做处理, tty driver
就是后面要讲解的
tty设备
, 在文件中讲过 unix 的万物皆文件
概念, tty 设备在 UNIX 环境下也是一个个独立的文件, 具体见ls -l /dev/tty*
. 用户进程就是
通过 tty 设备和内核进行input/output
信息的交换工作的.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| +----------------+ | TTY Driver | | | | +-------+ | +----------------+ +------------+ | | |<---------->| User process A | | Terminal A |<--------->| ttyS0 | | +----------------+ +------------+ | | |<---------->| User process B | | +-------+ | +----------------+ | | | +-------+ | +----------------+ +------------+ | | |<---------->| User process C | | Terminal B |<--------->| ttyS1 | | +----------------+ +------------+ | | |<---------->| User process D | | +-------+ | +----------------+ | | +----------------+
|
当外部中断设备请求连接时, 驱动会根据型号
和参数
创建相应的tty设备
, 之后中断设备就能通过
该设备文件ttyS0
来运行应用, 比如启动用户进程 A 等等, ttyS0
在这之间起到代理作用, 其信息交换
图如下:
1 2 3 4 5 6
| +--------+ Input +--------------------------+ R/W +------+ | +----------->+ +<---------->+ bash | |terminal| | pts/1 or ttys001 | +------+ | +<-----------+ +<---------->+ lsof | +--------+ Output | Foreground process group | R/W +------+ +--------------------------+
|
- Foreground process group: 记录当前
前台进程组
- Input(比如键盘)输入时, pts 会获取
当前前台进程组首会话
, 并将输入放入该进程输入缓存
中
- pts 设备发现数据输入时, 会将相应数据回传 Output(显示器)
在这中间, 可能一个 tty 会运行多个进程(比如多个后台进程), 此时如果有 IO 中断, 则需要进行一定的
阻塞. 这里的pts
实际上为网络终端登录
技术兴起后产生的, 其和 tty 的区别有如下几点:
- 本地和远程: pts 是远程网络登录, 比如 sshd 等, 而 tty 一般为计算机直连设备或者本机模拟设备
- 远端: pts 远程连接 ptmx, tty 的直接连接内核的
终端模拟器
, 但两者都负责维护会话和转发数据包.
- 本地端: ptmx 的另一端连接着用户空间应用程序(比如 ssh), 内核终端模拟器的另一端连接着具体的硬件
关于进程组
以及首会话
, 见笔记进程会话
ptmx
就是伪终端-pseudo terminal
, 现在让我们看下系统中各个 tty 设备输出, 并且进行一定的测试来
验证上面的结论.
1 2
| ps -ef|grep ttys|grep -v grep|grep -v zsh
|
其输出如下:
1 2 3 4 5 6 7
| 501 67156 67154 0 9:32上午 ?? 0:00.01 sshd: bamboounuse@ttys005 ====================================1==================================== 501 56192 278 0 二05下午 ttys000 0:00.10 /Applications/iTerm.app/Contents/MacOS/iTerm2 --server login -fp bamboounuse 0 56193 56192 0 二05下午 ttys000 0:00.37 login -fp bamboounuse ====================================2==================================== 0 67348 67346 0 9:33上午 ttys006 0:00.06 login -pf bamboounuse ====================================3====================================
|
关于login
进程, 会在后面第三章的终端登录和网络登录
中讲解. 上面三种不同类型的输出:
- 第一种类型是
远端xshell
客户端通过 ssh 连接本机, 可以看出 sshd 进程
- 第二种类型是
本机item2
打开的 shell 终端
- 第三种类型是
本机系统自带terminal
打开的 shell 终端
现在我们获取这些 ttys 进程相关的设备, 以便进行IN/OU
测试:
1 2 3 4 5 6 7
| ps -ef|grep ttys005|grep -v grep
sudo lsof -p <pid>
lsof /dev/ttys005
|
在找到各个 tty 设备后, 现在开始进行输入输出验证:
1 2 3 4 5 6
| echo "我是ttys001, 我现在向ttys005进行输入" >> /dev/ttys005
echo "我是ttys001, 我现在向ttys006 terminal原始终端输入" >> /dev/ttys006
|
现在让我们看下远端云服务器上的 tty 输出:
另外, MacOS 不同于 ubuntu, 后者的tty001~tty006
在开机之后就自动的创建, 当然, 如果没有使用
Ctrl + Alt + F1~F6
的话, 实际上找不到对应 login 进程.
tty 的创建逻辑
键盘直连
键盘,显示器和内核中的终端模拟器
直连, 由模拟器根据输入来决定启动那个 tty 设备. 比如, 输入
ctrl+alt+f1
时, 模拟器会根据输入转发给 tty1 的getty
程序(见第三章说明), 开启login:
操作
流程, 界面会自动切换到 tty1 终端登录上.
1 2 3 4 5 6 7 8 9 10 11
| +-----------------------------------------+ | Kernel | | +--------+ | +----------------+ +----------+ | +-------------------+ | tty1 |<---------->| User processes | | Keyboard |--------->| | +--------+ | +----------------+ +----------+ | | Terminal Emulator |<->| tty2 |<---------->| User processes | | Monitor |<---------| | +--------+ | +----------------+ +----------+ | +-------------------+ | tty3 |<---------->| User processes | | +--------+ | +----------------+ | | +-----------------------------------------+
|
图形界面的键盘直连
上面所述的直连方式时直接从图形界面切换到虚拟控制台
(见 1.5 节)的流程, 那么如果通过item2
等
终端模拟器的流程是怎样的呢?
大体流程类似, 不过引入了ptmx
来作为终端模拟器
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| +----------+ +------------+ | Keyboard |------>| | +----------+ | Terminal |--------------------------+ | Monitor |<------| | fork | +----------+ +------------+ | | ↑ | | | | write | | read | | | | +-----|---|-------------------+ | | | | | ↓ | ↓ | +-------+ | +-------+ | +--------+ | pts/0 |<---------->| shell | | | | +-------+ | +-------+ | | ptmx |<->| pts/1 |<---------->| shell | | | | +-------+ | +-------+ | +--------+ | pts/2 |<---------->| shell | | +-------+ | +-------+ | Kernel | +-----------------------------+
|
关于 ptmx 的通信流程,见下节网络登录 SSH 的讲解. 上面两个连接方式都是基于本地的连接, 都会自动的
创建login
来进行用户认证工作, 后面的网络登录则依托于网络登录协议认证机制.
远程登录 ssh
远端客户终端通过特殊的协议来和本服务器进行连接不仅仅是通过protocolClient
和protocolServer
,
实际上也是基于ptmx
来完成终端模拟器工作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| +----------+ +------------+ | Keyboard |------>| | +----------+ | Terminal | | Monitor |<------| | +----------+ +------------+ | | ssh protocol | ↓ +------------+ | | | ssh server |--------------------------+ | | fork | +------------+ | | ↑ | | | | write | | read | | | | +-----|---|-------------------+ | | | | | ↓ | ↓ | +-------+ | +-------+ | +--------+ | pts/0 |<---------->| shell | | | | +-------+ | +-------+ | | ptmx |<->| pts/1 |<---------->| shell | | | | +-------+ | +-------+ | +--------+ | pts/2 |<---------->| shell | | +-------+ | +-------+ | Kernel | +-----------------------------+
|
现在我们将建立连接
和消息收发
分开来讨论, 详细的讲解两个过程中消息的流动. 先看一下连接是
如何建立的.
- Terminal 根据指定协议请求连接 SSHD, 若验证通过则会创建一个新的 session(前段进程组)
- sshd 充当代理人角色, 调用 API, 请求 ptmx 创建一个
pts
并创建相应的文件描述符fd
- 最后只要将
0, 1, 2, N1
描述符和 session 关联在一起, 即完成连接过程, 当然还伴随这 shell 的绑定.
那么消息收发是怎样一个流程呢?
- 键盘输入通过协议到达远端服务器 sshd, 找到客户端 session 对应的 fd 开始进行写入(read)操作
- ptmx 在收到消息之后会将
数据包
转发到对应
的 pts 上.
- pts 在检查数据包之后, 联系前段进程组, 将数据发完首进程(leader), 最后交由 shell
- shell 在处理输入之后, 将回传结果写入 pts(write), pts 转发数据包到 ptmx
- ptmx 在找到 pts 对应的 fd, 进行数据写入操作
- sshd 会找到对应的 session 将数据通过协议回传给远端客户端
关于会话, 首进程等, 见文章会话.
tmux
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| +----------+ +------------+ | 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 |<--------------------------------------------+ | | +-------------+
|
终端登录和网络登录
终端登录
在第二章我们讲解了本地登录和网络登录过程中终端模拟器
或者虚拟控制台
所起到的作用, 那么
这个章节就从进程的角度来简单的了解下这些过程中都启动了哪些应用.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| +------+ | init | +--+---+ | |fork | +---------------------------------+ +--v---+ |Get Fd, tty message, environment.+-------------+ | init | +---------------------------------+ | +--+---+ | +-----------------+ | | | |new login request| | |exec | +----------------------+ | | | | | +--v--------+ | +----------------------------------------+ | | getty| | | 1 chdir. | | +--+---+ | | 2 chown. change the owner of terminal | | | | | 3 change permission and set shell, env | | |exec+-----+ | 4 run shell | | | +------+----------------------------------------+ | +--v---+ | | | login| | | +--+---+ | +-----------------------------------------+ | | | ptmx or tty device.| | | | fd: 0, 1, 2 | +--v------+ +--------------------+ | shell| | +-----------------+
|
这里本地终端登录不仅仅包含虚拟控制台
, 也包含现在带计算机中的各种终端模拟器
, 后者
实际上在内核中也用到ptmx伪终端
来连接终端设备和 shell.
网络登录
网络登录不同于本地终端登录, 其使用 Client/Server 来进行验证和通信, 所有登录都经由内核的
网络驱动程序
来进行, 事先并不知晓有多少这样登录, 通过协议设置某种公开的服务来等待网络
请求的到达.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| +------+ | init | +--+---+ | |fork | +--v---+ | init | running sshd service. Waitting client +--+---+ connect. | |exec | +--v---+ call api. | sshd | send session id +--+---+--------------->+---------+ | | ptmx | |fork +---------+ | | +---v----+ | | session| bind session and fd +---+-----<--------------+ | |exec +--v---+ | shell| +------+
|
参考链接
文章参考:
本博客其他相关文章: