%% # 纲要 > 主干纲要、Hint/线索/路标 # Q&A #### 已明确 ![[02-开发笔记/11-Linux/Linux-终端 terminal#^4hbi5i]] ![[02-开发笔记/11-Linux/Linux-终端 terminal#^uwqhen]] ![[02-开发笔记/11-Linux/Linux-终端 terminal#^zer2ui]] #### 待明确 > 当下仍存有的疑惑 **❓<font color="#c0504d"> 有什么问题?</font>** # Buffer ## 闪念 > sudden idea ## 候选资料 > Read it later %% ![[_attachment/02-开发笔记/11-Linux/Linux-终端 terminal.assets/IMG-Linux-终端 terminal-D8F3BC09643C3B5EA50597A8E5C4626C.png|638]] # "终端" 与 "控制台" 的历史背景及由来 > <font color="#c0504d"> ❓ "终端" 、 "控制台" 的历史由来及含义是什么? </font> ^4hbi5i 在《Unix & Linux 大学教程》的**第 3 章 Unix 连接**以及**第 6 章**、**第 7 章**中涉及了对 "终端"、"控制台"、"虚拟控制台" 历史由来的通俗介绍,非常赞! ## 终端 Terminal 一台**可供使用的计算机**包括两个部分[^1]: - **==主机==**(**机箱部分**,包括主板、硬盘、CPU、GPU、内存、网卡等) - **==终端==**(**输入输出设备**,包括显示器、键盘、鼠标、扬声器、麦克风等) 其中,「**终端**」指的是**与主机直接连接的**、用于**供人和主机交互**的 **==实体物理设备==**。 早期的 "**终端**" 是 "**电传打字机**" Teletyperwriter(缩写 `tty`),其只具备两个功能:**接受输入和打印输出**。 **Linux 系统中的 "设备文件名 `tty`"** 以及 " **`tty` 命令**" 正是取自 "Teletyperwriter" 这一名称。 ![[_attachment/02-开发笔记/11-Linux/Linux-终端 terminal.assets/IMG-Linux-终端 terminal-4166BE78676D8B8DF41FAA86E31BED6C.png|761]] ## 控制台 Console ![[_attachment/02-开发笔记/11-Linux/Linux-终端 terminal.assets/IMG-Linux-终端 terminal-1E3A12AF091574B41A8DD87531045B8C.png|458]] Linux 是多任务**多用户**系统,为了能**供多个用户同时登录使用一台主机**,需要**为一台主机配备多台物理终端设备**。 **其中一台终端**需要被用于**管理主机系统**,因而**视作为主机不可或缺的一部分**,这台特殊终端被称之为 "**==控制台==**(console)",**一台主机只有一个控制台**。 - 在系统启动时,**内核消息和启动日志会输出到控制台**; - 在操作计算机的过程中,与终端不相关的信息,比如**内核消息,后台服务消息**,也会显示到**控制台**上。 > [!example] > **以一台装有 Linux 系统的现代笔记本电脑为例**[^1],机身内置的屏幕、键盘、触控板、扬声器即为 "**终端**",而该终端也即是这台主机的 "**控制台**" <br> ## 「终端模拟器」与「伪终端」 - 当在一台**笔记本电脑**上运行 Linux 时,**终端内置于机身**,与主机直接相连; - 当在一台**桌面 PC**上运行 Linux 时,**终端通过线缆连接到主机**。 在上述情况下,"**终端**" 由屏幕、键盘、鼠标、扬声器与麦克风等**物理输入输出设备**构成[^1],同时 **"终端" 本身即是 "控制台"**。 当 "**远程连接到一台 Linux 主机** 时: - 对于**连接远程主机的用户**侧而言: 由「**==终端模拟器==**」来模拟物理终端,为用户提供 "**命令行界面**",称之为 "**==终端窗口==**"; - 对于**远程主机上的 Linux 系统**侧而言:由「**==伪终端==**」来模拟物理终端,其充当**终端模拟器及其他应用程序**与**命令行程序**(shell)之间的中介。 \ > [!NOTE] 终端模拟器与伪终端的关联 > > "**终端模拟器**" 是**用户侧的程序**,需要结合 **Linux 系统侧**的 "**伪终端 (PTY) 设备**" 进行工作。 > > - 「终端模拟器」面向用户 "模拟物理终端",**对用户提供 "终端窗口" 以供输入和输出**。 > - 「伪终端」面向 Linux 系统 "模拟物理终端",供应用程序(如终端模拟器)交换输入输出 <br><br><br> # "终端" 相关语总览 > ❓<font color="#c0504d">终端、控制台、虚拟终端、虚拟控制台、终端模拟器、伪终端分别指代什么?</font> ^uwqhen ![[_attachment/02-开发笔记/11-Linux/Linux-终端 terminal.assets/IMG-Linux-终端 terminal-6B16C45DE0E9EFA385E1A4D426840D2F.png|552]] - **终端**(terminal):一个抽象的泛称。 - 一方面,指代**连接到物理主机的物理输入输出设备**,例如最早期的电传打字机和 VT100,现代的**显示器、键盘、鼠标、触控板、扬声器、麦克风等**。 - 另一方面,也常用于指代 Unix/Linux 系统中体现在**用户侧**的"**虚拟终端**"—— "**终端模拟器**"、"**虚拟控制台**" 。 - **控制台**(console):**系统所在==物理主机==上直连的物理终端设备**(显示器、键盘),也即 "**物理控制台**"。 - **虚拟终端**:泛指,包括 "**==终端模拟器==**"、"**==伪终端==**"、"**==虚拟控制台==**" - **虚拟控制台**(virtual console):由 Linux 内核提供的机制,用于在**物理控制台**(单个物理显示器和键盘)上**运行和切换多个独立的终端会话**,**模拟多个物理控制台**。 - 在**物理控制台**上,可通过 `Ctrl + Alt + F1~F12` 启动至不同的**虚拟控制台**。 - 设备文件: - `/dev/tty1` ~ `/dev/tty12` 对应**各个独立的虚拟控制台** - `/dev/tty0` 指向 "**当前活动的虚拟控制台**" - **终端模拟器** | **终端仿真器**(terminal emulator):图形用户界面环境下提供的 "**终端窗口**" - 本身是一个 "**应用程序**",通过 "**伪终端 (PTY)**" 来实现其功能。 - **伪终端 (pseudo terminal,也称 pty,表示 "pseudo tty")**:**Linux 操作系统对 "终端" 的抽象**,体现为**一对虚拟终端设备文件**,包括 "**主设备** master" 和 "**从设备** slave"。 - 主设备文件为 `/dev/ptmx`; - 从设备文件为 `/dev/pts/` 目录下的各个文件 - 例如 `/dev/pts/0`、`/dev/pts/1` 等。 > [!NOTE] `/dev/tty` 与 `/dev/tty0` 的区别 > > ![[_attachment/02-开发笔记/11-Linux/Linux-终端 terminal.assets/IMG-Linux-终端 terminal-F31F7AFBDFBB85AC672633E9201A99F2.png]] > > - `/dev/tty` : 当前 "**进程**" 所关联的终端设备文件,可以是**虚拟控制台**(`/dev/tty1 ~ /dev/tty12`)或者 **伪终端** (`/dev/pts/n`)。 > - `/dev/tty0`:指向 "**当前==呈现==的虚拟控制台**"。也即**当前屏幕显示的虚拟控制台**("current display") > - `/dev/console`:在现代 Linux 系统上**默认**同 "`/dev/console`"。 > > > [!example] > > > > 当执行一个命令 or 启动一个进程时,引用 `/dev/tty` 表示的是 "**该命令/进程所属终端**",**不受切换终端的影响**;而引用 `/dev/tty0` 时,则始终表示 "**当前屏幕显示的虚拟控制台**"。 > > > > 例如: > > > > - 在 `/dev/tty3` 下执行命令 `sleep 5; echo tty0 > /dev/tty0` 并切换到 `/dev/tty4`,则**输出内容将在 `/dev/tty4` 中显示**。 > > - 在 `/dev/tty3` 下执行命令 `sleep 5; echo tty > /dev/tty` 并切换到 `/dev/tty4`,则**输出内容不会在 `/dev/tty4` 中显示,只会显示在 `/dev/tty3` 中**。 > <br><br><br> # 终端 Terminal ![[_attachment/02-开发笔记/11-Linux/Linux-终端 terminal.assets/IMG-Linux-终端 terminal-440B06AA8FBA2F48D07A33811CC4AB7F.png]] > [!NOTE] 环境变量 `TERM` 记录了当前使用的"终端类型" > > > (1)桌面环境下提供的终端模拟器 > > ![[_attachment/02-开发笔记/11-Linux/Linux-终端 terminal.assets/IMG-Linux-终端 terminal-FA5BB44FF62AC8D2FEB84A408C5180D2.png|307]] > > (2)远程登录软件提供的终端模拟器 > > ![[_attachment/02-开发笔记/11-Linux/Linux-终端 terminal.assets/IMG-Linux-终端 terminal-670DB87815179AF1CB16681BD05022F0.png|443]] > > (3)虚拟控制台中 > ![[_attachment/02-开发笔记/11-Linux/Linux-终端 terminal.assets/IMG-Linux-终端 terminal-7C738C7E387B85372369828A458CAF78.png|503]] > <br><br><br> # 控制台 Console 「**控制台**」指代的是 **"Linux 系统" 所在==物理主机==上直连的==物理终端设备==**——**显示器、键盘**等,也称 "**物理控制台**"。 #### `/dev/console` 设备文件 `/dev/console` 为 "**系统控制台**" 设备文件,是**系统启动过程中和内核消息**的**主要输出设备**。 - 在**系统启动的==早期阶段==**,`/dev/console`自动关联到 **==物理终端==**,例如计算机的主屏幕和键盘,以便**在启动过程中提供信息和故障排除**。 - 在**系统启动**完成后,`/dev/console` 默认指向 "**当前活动的虚拟控制台**" 设备文件,同 `/dev/tty0`。 `/dev/console` 主要用于**接收系统消息**,包括**系统启动时的内核消息、启动日志和错误消息** 都将发送到该**设备文件**。 用户可以查看系统的启动日志和错误报告,但**不能直接在其上输入命令**。 ![[_attachment/02-开发笔记/11-Linux/Linux-终端 terminal.assets/IMG-Linux-终端 terminal-C52AD6F10BE276A16810732057B86C72.png|645]] <br><br> # 虚拟控制台(Virtual Console) ![[_attachment/02-开发笔记/11-Linux/Linux-终端 terminal.assets/IMG-Linux-终端 terminal-C66F50E1651EF0B85BC6C24F4EEB3DDD.png|656]] ![[_attachment/02-开发笔记/11-Linux/Linux-终端 terminal.assets/IMG-Linux-终端 terminal-EBC4EED7EC8D1DDA8A0FC8AD29631131.png|616]] 「**虚拟控制台**」是 Linux 系统内核是对 "**物理控制台**" 的一种 **抽象**,每个虚拟控制台都 **模拟一个独立的物理终端**,表现为 "**一个全屏的、独立的 ==图形用户界面 ==或者[[#命令行界面|命令行终端界面]]**",使得单个用户能够在**同一套==物理控制台设备==(屏幕、键盘)上运行多个独立的终端会话**。 Linux 内核提供了多个**虚拟控制台**: - 各个**虚拟控制台** 之间通过 **`Ctrl+Alt+ F1~F12` 快捷键**进行**切换**; - 命令行界面下 `Alt + F1~F12` 即有效,但在大多数 GUI 界面中需要加上 `Ctrl`。 - 各个**虚拟控制台** 对应的 **==设备文件==** 分别为 `/dev/tty1 ~ /dev/tty12`; - `/dev/tty0` 指向 "**当前活动的虚拟控制台**" 在 Linux 系统中,**图形用户界面 GUI** 运行在一个**专用的虚拟控制台**上(例如 `/dev/tty1` 和 `/dev/tty2`,取决于系统配置),而**命令行界面**的虚拟控制台则是运行在其他控制台上。当启动 Linux 系统并进入到 **桌面环境** 后,屏幕所见的即是 "**虚拟控制台**"。 > [!NOTE] 图形用户界面与 > > 在我所装的 Ubuntu 上,`Ctrl+Alt+F1/F2` 对应的**虚拟控制台**运行的都是 "**图形用户界面**" ,`Ctrl+Alt+F3/F4` 等运行的才是 "**命令行界面**"。 > > ![[_attachment/02-开发笔记/11-Linux/Linux-终端 terminal.assets/IMG-Linux-终端 terminal-3588861D9DD5F7274B3D35A5F38C8B64.png|733]] > > > [!NOTE] > - 使用命令 `chvt [n]` 可以切换到第 `n` 个虚拟控制台。 > - 使用命令 `openvt -s -w -- [command]` 可以在一个新的虚拟控制台上运行指定的命令。 <br><br> # 终端模拟器 (Terminal Emulator) **终端模拟器** 是一种 "**应用程序**",用于**模拟 Linux 主机的物理终端**,在 "**==图形用户界面==**" 环境和 "**==远程登录==**" 场景下为用户提供 **终端窗口**(**命令行窗口**)。 - **图形用户界面环境提供的 `gnome-terminal`、`xterm`、`konsole`** 等终端窗口 - **远程登录软件** `XShell` 等提供的终端窗口 上述**由应用程序提供的命令行窗口**即称之为 "**终端模拟器**"。 > [!quote] > GNOME Terminal 是**GNOME Shell 桌面环境的默认终端模拟器**。 > 包括 Red Hat Enterprise Linux(RHEL)、CentOS 和 Ubuntu 在内的很多发行版默认采用 GNOME Shell 桌面环境,自然也默认使用 GNOME Terminal。 <br> ### 终端模拟器的工作原理 ![[_attachment/02-开发笔记/11-Linux/Linux-终端 terminal.assets/IMG-Linux-终端 terminal-6B16C45DE0E9EFA385E1A4D426840D2F.png|627]] 「终端模拟器」通过「**伪终端**」实现其功能。 1. **创建伪终端对**: - 终端模拟器打开**主设备文件**(通常是 `/dev/ptmx`),操作系统为其分配一个**从设备**(如 `/dev/pts/0`)。 2. **与命令行程序交互**: - 终端模拟器 **监听用户的键盘输入**,与 **主设备**(**ptm**)通信; - 主设备通过 **TTY 驱动** 将用户的输入传递给**从设备(pts)** ,由**从设备运行命令行程序**(如shell)。 3. **显示输出**: - 命令行程序的输出通过**从设备**传递回**主设备**,终端模拟器读取这些输出,调用图形接口(比如 X11),将输出结果渲染至显示器。 <br><br><br> # 伪终端 (Pseudo Terminal) > **伪终端**(Pseudo-Terminal,**PTY**) 伪终端是 Linux 系统**对物理终端设备的一层==抽象==**,其通过模拟一个 "**==物理终端==**" 来提供终端服务,进而实现 "**虚拟终端会话**"。 伪终端具体表现为 Linux 系统中**一对==关联的设备文件==**,分为**主设备**(master)与 **从设备**(slave),分别代表**终端会话的主控和子控**。 - **主设备文件**(pesudoterminal master,**==ptm==**) - `/dev/ptmx`,系统级设备文件,**由所有终端会话==共享==一个主设备文件**。 - **从设备文件**(pesudoterminal slave,**==pts==**) - `/dev/pts/n`,其中 `n` 为从 0 起的数字,**每个伪终端会话==唯一对应==一个从设备文件** 伪终端没有直接关联的物理设备,而是**为应用程序提供终端形式的接口**,充当**终端模拟器等应用程序**与**命令行程序(shell)** 之间的中介。 对于连接到伪终端设备文件的应用程序而言,就会将其认为是 "**物理终端**"。 > [!NOTE] 通过 `man 4 ptmx ` 可查看关于伪终端中主、从设备的解释说明。 > > ![image-20230919142440822|989](_attachment/02-开发笔记/11-Linux/Linux-终端%20terminal.assets/IMG-Linux-终端%20terminal-B13B185AA9D5DD45DB7CFD0F71E2BD67.png) > <br> ## 伪终端的作用 1. 在**终端模拟器**方面: - 终端模拟器使用伪终端提供终端服务。每次打开一个新的终端窗口时,**终端模拟器会创建一个伪终端对**,并在其中运行一个shell。 2. 在**远程登录**方面: - 当用户通过SSH登录到远程系统时,**SSH 守护进程会打开一个伪终端对**,并中继**客户机上终端窗口**与该**伪终端**之间的输入输出。 3. 实现**终端复制: - 伪终端允许用户**在一个终端窗口中运行多个终端会话**。 - 例如 `screen` 和 `tmux`,可将输入和输出从**一个终端转播到另一个终端**,使文本模式的应用程序从实际的终端上脱离。 4. **串口通信:** - 伪终端可以用于与串口设备(例如调制解调器、串口终端)进行通信,允许数据在计算机和外部设备之间进行传输。 > [!faq] ❓为什么 Linux 中需要伪终端这一层抽象?shell 不可以直接从显示器和键盘读取数据吗? > > "伪终端" 的这一层抽象是为了**实现 "终端复制"(同屏运行多个终端模拟器以及同一终端窗口中运行多个会话)、实现远程登录**所必须的。 ^zer2ui <br> ## 伪终端的工作方式 >❓ <font color="#c0504d">伪终端的工作方式是什么?</font> 这种**共享主设备**、**唯一从设备**的结构允许**多个应用程序在不互相干扰的情况下创建和管理它们自己的伪终端会话,每个伪终端会话都有自己的输入和输出通道**。 - 应用程序通过**打开==主设备文件== `/dev/ptmx` 来请求创建新的伪终端会话**,且**允许多个程序同时打开该文件**; - 每个**伪终端会话都唯一关联一个==从设备文件**== 。设备文件名从 `/dev/pts/0` 起,数字依次递增。 - **主设备文件**用于与**应用程序**(如**SSH 守护进程或终端模拟器**)进行通信。 - **从设备文件**用于与**命令行程序( shell)** 进行通信。 --- - 「**主设备文件**」充当**伪终端会话的主控设备**,**供应用程序访问**,负责 **==创建新的 "从设备文件"== 并将其分配给应用程序**,作为**新的伪终端会话**; - 「**从设备文件」作为对 "物理终端" 的模拟**,应用程序使用**从设备文件的==文件描述符==** 来**访问伪终端的输入和输出**: #### 伪终端的工作原理 1. 当需要创建新的伪终端会话(`pty`)时,**应用进程(例如终端仿真器)打开 `/dev/ptmx` 主设备文件**。 2. 当进程打开 `/dev/ptmx` 文件时: 1. 操作系统会在 `/dev/pts/` 目录中为该进程创建一个**唯一关联的从设备文件**(**==pts==**),例如 `/dev/pts/0`。 2. 同时,返回该**从设备文件**的 **==文件描述符== FD**——称为 "**pseudoterminal master,==ptm==**"。 当存在伪终端会话时,**主设备文件 `/dev/ptmx` 始终保持打开状态**(可以认为在内存中存在一个 `ptmx` 对象)。 `/dev/ptmx` 在内部会**维护「==文件描述符 ptm==」 和 「==从设备文件 pts==」 的对应关系**, ![image-20230919113422818](_attachment/02-开发笔记/11-Linux/Linux-终端%20terminal.assets/IMG-Linux-终端%20terminal-961D9B1D30ECCAEA2E8483D88DED69C1.png) > [!NOTE] `sudo lsof /dev/ptmx` 可以查看 ptmx 打开的文件描述符。 > > ![image-20230919135849883|656](_attachment/02-开发笔记/11-Linux/Linux-终端%20terminal.assets/IMG-Linux-终端%20terminal-1BC69958C84AC4A0C1767A87A95F590A.png) > %% ## TODO #TODO **==没弄懂、暂时搁置==**。 键盘的输入设备:`/dev/input/event0` 等,表示键盘事件的输入。 鼠标的输入设备:`/dev/input/mouse0` 等,表示鼠标移动和点击事件 - 键盘输入事件通过输入设备文件(如 `/dev/input/eventX`)传递给内核。 - 内核将这些输入事件传递给当前活动的终端设备(如 `/dev/tty1`)。 - 虚拟控制台(如 `/dev/tty1`)读取键盘输入并在显示器上显示输出。 - 应用程序通过主设备与从设备之间传递数据,主设备负责接收用户的输入数据并传递给从设备,从设备将数据传递给运行在其中的应用程序(如shell)。 - 用户在从设备中运行的应用程序(如shell)认为它正在与一个真实的物理终端进行交互,而实际上它是在与伪终端进行交互。 - 终端模拟器打开主设备(通常是 `/dev/ptmx`),系统为其分配一个从设备(如 `/dev/pts/0`)。 - **与命令行程序交互**: - 终端模拟器与主设备通信,将用户的输入传递给从设备,从设备运行命令行程序(如shell)。 - **显示输出**: - 命令行程序的输出通过从设备传递回主设备,终端模拟器读取这些输出并在窗口中显示。 **tty驱动**是内核的一部分,管理所有**终端设备文件**的输入输出操作 - 如果用户当前在本地物理控制台上,tty驱动将事件传递给对应的虚拟控制台设备文件(如 `/dev/tty1`)。 - **命令行程序处理**: - 虚拟控制台设备文件将输入事件传递给正在运行的命令行程序(如shell)。 - shell接收输入并等待用户输入的命令。 - **伪终端设备文件**: - 如果用户通过SSH或终端模拟器进行远程连接,事件通过伪终端的主设备(如 `/dev/ptmx`)传递给对应的从设备(如 `/dev/pts/0`)。 - **命令行程序处理**: - 从设备将输入事件传递给运行在伪终端上的命令行程序(如shell)。 - shell接收输入并等待用户输入的命令。 用户键盘输入 --> 键盘设备文件(/dev/input/eventX) --> 内核输入子系统(`evdev`驱动接收键盘事件) --> tty驱动 --> 终端设备文件(/dev/ttyX或/dev/pts/X) --> shell处理输入 --> shell执行命令并输出 --> 终端设备文件(/dev/ttyX或/dev/pts/X) --> tty驱动处理输出 --> 显示器输出(物理显示器或终端模拟器窗口) --- ##### 1. 创建伪终端对 1. 当需要创建新的伪终端会话(`pty`)时,**应用进程(例如终端仿真器)打开 `/dev/ptmx` 主设备文件**。 2. 当进程打开 `/dev/ptmx` 文件时: 1. 操作系统会在 `/dev/pts/` 目录中为该进程创建一个**唯一关联的从设备文件**(**==pts==**),例如 `/dev/pts/0`。 2. 同时,返回该**从设备文件**的 **==文件描述符== FD**——称为 "**pseudoterminal master,==ptm==**"。 其中, - **主设备文件**用于与**应用程序**(如**SSH 守护进程或终端模拟器**)进行通信。 - **从设备文件**用于与**命令行程序( shell)** 进行通信。 ##### 2. 交互 用户在键盘上按下按键,键盘硬件生成一个 "键盘事件",写入到键盘设备文件(例如 `/dev/input/event0`)中。 内核中的输入子系统 # **串行端口终端** - 对应设备文件为 `/dev/ttyS1 ~ /dev/ttySN`;其中 N 为从 0 开始的数字。 > ![image-20230919160346197](_attachment/02-开发笔记/11-Linux/Linux-终端%20terminal.assets/IMG-Linux-终端%20terminal-8042F6BE4558141C18641998576FCA93.png) > ![image-20221102143648033|705](_attachment/02-开发笔记/11-Linux/Linux-终端%20terminal.assets/IMG-Linux-终端%20terminal-E2A1F7DF0EAB2AC9FFF6311863FCA1E7.png) --- %% <br><br> # `tty` 命令 `tty` 命令打印 **连接到当前 shell 的==标准输入(`stdin`)==** 的 **终端设备文件名**: - 在**虚拟控制台**下,将显示**虚拟控制台设备文件**: `/dev/tty1 ~ /dev/ttyN`。 - 在**终端模拟器** (gnome-terminal / Xshell 远程登录) 下,将显示**伪终端的从设备文件** `/dev/pts/N`。 > [!example] > > (1)虚拟控制台下: > > ![image-20230919134508888|385](_attachment/02-开发笔记/11-Linux/Linux-终端%20terminal.assets/IMG-Linux-终端%20terminal-A5563DEF59F10CC3ACB57735EECB143B.png) > > (2)图形界面环境提供的终端模拟器下: > > ![image-20230919144922193|573](_attachment/02-开发笔记/11-Linux/Linux-终端%20terminal.assets/IMG-Linux-终端%20terminal-1C8492190958BD0B679EDC325BD73D57.png) > > > (3)远程会话软件 Xshell 提供的终端模拟器下: > > ![image-20230919144729928|593](_attachment/02-开发笔记/11-Linux/Linux-终端%20terminal.assets/IMG-Linux-终端%20terminal-421857AB5481332D4BFA5FEEBCAC5136.png) > > [!NOTE] 如果**有多个用户**远程连接到 Linux 机器,可以使用 `who` 命令来检查其他用户连接到的是哪个 TTY。 > > ![image-20230919155606460](_attachment/02-开发笔记/11-Linux/Linux-终端%20terminal.assets/IMG-Linux-终端%20terminal-EB5C97BD313C68D8600486C38FE3CD46.png) > 命令 `ps -ax` 可以**查看所有程序以及对应的终端**。 通过这个命令,发现如果多开一个 `screen` 程序,或者用 `telnet` 登陆之后,那么机器上面就会多一个 `/dev/pts/*` 。 --- <br><br> # 虚拟终端之间的消息传递 #### (1) 伪终端之间的消息传递 在伪终端`/dev/pts/1`下,向另一个伪终端`/dev/pts/0`发生信息 `"test /dev/pts/0"`。 ![image-20230919161006475|633](_attachment/02-开发笔记/11-Linux/Linux-终端%20terminal.assets/IMG-Linux-终端%20terminal-D5F99F61EBCEB7AE8B0F45375F16F7E9.png) 伪终端`/dev/pts/0` 收到的消息,只作为 "**该终端的输出**",而**不会被当作该终端的输入**。 即如果写入消息 `"echo -n ls > /dev/pts/0"`,在`/dev/pts/0` 下也只是**输出该字符串**,而不会执行命令 ls。 ![image-20230919161619111|644](_attachment/02-开发笔记/11-Linux/Linux-终端%20terminal.assets/IMG-Linux-终端%20terminal-5040CCA98BE6D8FCAC813489C93CD989.png) ![image-20230919161811546|653](_attachment/02-开发笔记/11-Linux/Linux-终端%20terminal.assets/IMG-Linux-终端%20terminal-38F999C60A6B003BDFAB41EEE773E7AC.png) #### (2)虚拟控制台之间的消息传递 ![image-20230919163133947](_attachment/02-开发笔记/11-Linux/Linux-终端%20terminal.assets/IMG-Linux-终端%20terminal-34B8B1F5225EC49127DDF0E800F424D3.png) --- %% # 终端与 shell 的交互过程 #### (1)在 Linux 图形桌面的终端模拟器下 在 Linux 图形桌面上打开的终端,称为**终端模拟器 / 终端仿真器 / 虚拟终端**,它是**用软件的方式来模拟一个终端设备**,其的功能就是访问 Unix shell 如果使用的是 gnome 终端,可通过命令 `man gnome-terminal` 查看关于 gnome-terminal 的说明 ![img|690](_attachment/02-开发笔记/11-Linux/Linux-终端%20terminal.assets/IMG-Linux-终端%20terminal-CC9365262B3E45B894E49C16F43D9E00.webp) 在 Linux 图形桌面的终端模拟器下,完整的命令交互过程: -> 访问 **terminal / 终端 / terminal emulator / 终端仿真器 / 终端模拟器 / 虚拟终端 ** -> 关联 **伪终端 pty (pseudo tty)**(`/dev/ptmx` ,`/dev/pts/N`) -> **shell**(包括 bash、sh、zsh 等) -> **kernel** 具体来说,terminal 执行一个命令的全过程如下: > 1. 我们在桌面启动终端程序 `gnome-terminal`,它向操作系统请求一个`PTY master`,并把 GUI 绘制在显示器上 > > 2. `gnome-terminal` 启动子进程 `bash` > > 3. `bash` 的标准输入、标准输出和标准错误都设置为 `PYT slave` > > 4. `gnome-terminal` 监听键盘事件,并将输入的字符发送到`PTY master` > > 5. **line discipline** 收到字符,进行缓冲。只有当你按下回车键时,它才会把缓冲的字符复制到`PYT slave`。 > > 6. **line discipline** 在接收到字符的同时,也会把字符写回给`PTY master`。`gnome-terminal` 只会在屏幕上显示来自 `PTY master` 的东西。因此,**line discipline** 需要回传字符,以便让你看到你刚刚输入的内容。 > > 7. 当你按下回车键时,TTY 驱动负责将缓冲的数据复制到`PYT slave` > > 8. bash 从标准输入读取输入的字符(例如 `ls -l` )。注意,bash 在启动时已经将标准输入被设置为了`PYT slave` > > 9. bash 解释从输入读取的字符,发现需要运行 `ls` > > 10. bash fork 出 ls 进程。bash fork 出的进程拥有和 bash 相同的标准输入、标准输出和标准错误,也就是`PYT slave` > > 11. ls 运行,结果打印到标准输出,也就是`PYT slave` > > 12. TTY 驱动将字符复制到`PTY master` > > 13. `gnome-terminal` 循环从 `PTY master` 读取字节,绘制到用户界面上。 #### (2)在 Xshell 等远程终端模拟器下 我们经常通过 ssh 连接到一个远程主机,这时候远程主机上的 `ssh server` 就是一个伪终端 PTY,它同样持有 `PTY master`,但 `ssh server` 不再监听键盘事件,以及在屏幕上绘制输出结果,而是通过 TCP 连接,向 `ssh client` 发送或接收字符。 > ![image-20230919164847452](_attachment/02-开发笔记/11-Linux/Linux-终端%20terminal.assets/IMG-Linux-终端%20terminal-6C1371F98260FE23D492DB27331BAC9F.png) 远程终端是如何执行命令的: > 1. 用户在客户端的 terminal 中输入 ssh 命令,经过 `PTY master`、TTY 驱动,到达 `PTY slave`。bash 的标准输入已经设置为了 `PTY slave`,它从标准输入读取字符序列并解释执行,发现需要启动 ssh 客户端,并请求和远程[服务器](https://cloud.tencent.com/act/pro/promotion-cvm?from=20067&from_column=20067)建 TCP 连接。 > 2. 服务器端接收客户端的 TCP 连接请求,向内核申请创建 PTY,获得一对设备文件描述符。让 `ssh server` 持有 `PTY master`,`ssh server` fork 出的子进程 bash 持有 `PTY slave`。bash 的标准输入、标准输出和标准错误都设置为了`PTY slave`。 > 3. 当用户在客户端的 terminal 中输入命令 `ls -l` 和回车键,这些字符经过 `PTY master` 到达 TTY 驱动。我们需要禁用客户端 **line discipline** 的所有规则,也就是说客户端的 **line discipline** 不会对特殊字符回车键做处理,而是让命令 `ls -l` 和回车键一起到达 `PTY slave`。`ssh client` 从 `PTY slave` 读取字符序列,通过网络,发送给 `ssh server`。 > 4. `ssh server` 将从 TCP 连接上接收到的字节写入`PTY master`。TTY 驱动对字节进行缓冲,直到收到特殊字符回车键。 > 5. 由于服务器端的 **line discipline** 没有禁用 `echo` 规则,所以 TTY 驱动还会将收到的字符写回`PTY master`,`ssh server` 从 `PTY master` 读取字符,将这些字符通过 TCP 连接发回客户端。注意,这是发回的字符不是 `ls -l` 命令的执行结果,而是 `ls -l` 本身的回显,让客户端能看到自己的输入。 > 6. 在服务器端 TTY 驱动将字符序列传送给 `PTY slave`,bash 从 `PTY slave`读取字符,解释并执行命令 `ls -l`。bash fork 出 `ls` 子进程,该子进程的标准输入、标准输出和标准错误同样设置为了 `PTY slave`。`ls -l` 命令的执行结果写入标准输出 `PTY slave`,然后执行结果通过 TTY 驱动到达 `PTY master`,再由 `ssh server` 通过 TCP 连接发送给 `ssh client`。 注意在客户端,我们在屏幕上看到的**所有字符都来自于远程服务器**。包括我们输入的内容,也是远程服务器上的 **line discipline** 应用 `echo` 规则的结果,将这些字符回显了回来。表面看似简单的在远程终端上执行了一条命令,实际上底下确是波涛汹涌。 #### (3)在控制台终端 / 虚拟控制台下 -> 访问 **虚拟控制台 / virtual console 控制台终端** ( `/dev/ttyN` ) -> **shell**(包括 bash、sh、zsh 等) -> **kernel** > 注: > > - **终端模拟器会和伪终端关联**,对应伪终端设备文件。 > > 在伪终端下启动的进程,该进程的标准输入、标准输出和标准错误都输出都会**绑定到伪终端设备文件**`/dev/pts/N` 上 > > - **虚拟控制台/控制台终端**直接对应设备文件,不经过伪终端。 > > 在虚拟控制台 / 控制台终端下启动的进程,该进程的标准输入、标准输出和标准错误绑定在虚拟控制台设备文件`/dev/ttyN`上。 %% <br><br> # 命令行界面 > **命令行界面** (command line interface,CLI) 「命令行界面」由 "**==虚拟控制台==**" 或 "**==终端模拟器==**" 提供[^1]。 ![[_attachment/02-开发笔记/11-Linux/Linux-终端 terminal.assets/IMG-Linux-终端 terminal-947E539E1634EEBAF8F76C5FFCB9A00A.png|628]] #### (1) **虚拟控制台中的命令行界面** 由操作系统**内核直接提供**的**全屏的命令行终端界面**,是与 Linux 系统交互的直接接口。 在与 "Linux 系统所在物理主机" 直连的显示器屏幕上所显示的,就是关联于 "虚拟控制台" 的**图形用户界面**或**命令行界面**。 ![[_attachment/02-开发笔记/11-Linux/Linux-终端 terminal.assets/IMG-Linux-终端 terminal-3588861D9DD5F7274B3D35A5F38C8B64.png|733]] #### (2)终端模拟器所提供的命令行界面 例如 Linux 图形桌面环境 GNOME 提供的 `GNOME Terminal` 终端模拟器,即提供了一个**命令行窗口**。 ![[_attachment/02-开发笔记/11-Linux/Linux-终端 terminal.assets/IMG-Linux-终端 terminal-28DE21C7483D0F45F731F89512BC23DE.png|712]] <br><br> # 参考资料 [linux 终端控制](https://fafucoder.github.io/2021/03/30/linux-terminal/) [What is the exact difference between a 'terminal', a 'shell', a 'tty' and a 'console'?](https://unix.stackexchange.com/questions/4126/what-is-the-exact-difference-between-a-terminal-a-shell-a-tty-and-a-con) [Linux 终端(TTY)](https://www.cnblogs.com/sparkdev/p/11460821.html) [理解Linux 终端、终端模拟器和伪终端 ](https://cloud.tencent.com/developer/news/841435) :star: [Unix 终端系统(TTY)是如何工作的](https://waynerv.com/posts/how-tty-system-works/) # Footnotes [^1]: [Unix & Linux 大学教程(Harley Hahn's Guide to Unix and Linux)](https://www.harley.com/unix-book/book/chapters/03.html)