LinuxTTY 子系统2(基于Linux6.6)---tty架构
一、 软件架构
Linux kernel TTY framework位于“drivers/tty”目录中,其软件框架如下所示:
Linux其它的framework类似,TTY framework通过TTY core屏蔽TTY有关的技术细节,对上以字符设备的形式向应用程序提供统一接口,对下以TTY device/TTY driver的形式提供驱动程序的编写框架。
1.1、TTY Core
TTY core是TTY framework的核心逻辑,功能包括:
1)以字符设备的形式,向用户空间提供访问TTY设备的接口,例如:
设备号(主, 次) 字符设备 备注
(5, 0) /dev/tty 控制终端(Controlling Terminal)
(5, 1) /dev/console 控制台终端(Console Terminal)
(4, 0) /dev/vc/0 or /dev/tty0 虚拟终端(Virtual Terminal)
(4, 1) /dev/vc/1 or /dev/tty1 同上
… … …
(x, x) /dev/ttyS0 串口终端(名称和设备号由驱动自行决定)
… … …
(x, x) /dev/ttyUSB0 USB转串口终端
… … …
2)通过设备模型中的struct device结构抽象TTY设备,并通过struct tty_driver抽象该设备的驱动,并提供相应的register接口。TTY驱动程序的编写,简化为填充并注册相应的struct tty_driver结构。
注2:TTY framework弱化了TTY设备(图片1中使用虚线框标注)的概念,通常情况下,可以在注册TTY驱动的时候,自动分配并注册TTY设备。
3)使用struct tty_struct、struct tty_port等数据结构,从逻辑上抽象TTY设备及其“组件”,以实现硬件无关的逻辑。
4)抽象出名称为线路规程(Line Disciplines)的模块,在向TTY硬件发送数据之前,以及从TTY设备接收数据之后,进行相应的处理(如特殊字符的转换等)。
1.2 System Console Core
在 Linux 内核中,system console(系统控制台)主要有两个功能:
1. 显示内核信息
系统控制台用于输出内核启动过程中的各种信息。具体来说,当 Linux 内核启动时,会将内核的日志信息(包括启动时的初始化过程、驱动加载、硬件检测等)打印到控制台设备上。这些信息对调试和系统诊断非常重要。
- 启动信息:系统控制台会显示内核引导信息,例如内核版本、硬件信息、磁盘驱动加载情况、网络接口配置等。
- 错误信息:当系统发生错误或崩溃时,系统控制台也会显示相关的错误信息,帮助系统管理员进行问题定位。例如,如果发生内核恐慌(Kernel Panic),错误信息会被打印到控制台。
通过控制台输出,管理员可以在没有图形界面的情况下,看到系统的运行状态和错误日志。通常,内核会通过虚拟终端(如 /dev/tty0
)或者串行端口(如 /dev/ttyS0
)来输出这些信息。
2. 与用户进行交互
系统控制台还可以作为一个与内核交互的接口,允许系统管理员在系统出现问题时执行一些紧急操作或修复。例如:
-
中断控制台操作:在某些情况下,内核可能会通过控制台显示一个提示信息,等待用户的输入(例如,在发生严重错误时)。管理员可以使用控制台来输入调试命令或选择恢复操作。
-
远程控制台管理:在服务器和嵌入式系统中,系统控制台还可以通过串口或网络进行远程管理(如通过
SSH
登录或串行控制台进行交互)。通过远程控制台,管理员可以在系统无法正常启动时(比如没有图形界面)访问并诊断系统。
1.3 TTY Line Disciplines
TTY Line Discipline 的基本概念
TTY Line Discipline(简称 LDISC)可以理解为一组负责终端数据处理和流控制的内核模块。每个终端设备(如 /dev/tty
、/dev/pts
等)都与一个 TTY Line Discipline 相关联,用于执行对终端数据的操作,包括输入、输出、特殊字符处理等。
在 Linux 中,TTY Line Discipline 可以理解为类似于驱动程序的功能模块,每个终端设备都有一个 LDISC 模块来定义其行为。
主要的 TTY Line Disciplines
Linux 内核中有多种 TTY Line Discipline,用于处理不同类型的终端或特定场景的需求。最常见的 TTY Line Discipline 包括:
1. 标准行规程(canonical line discipline, LDISC 0)
这是最常见的 LDISC 类型,也叫“标准行规程”或“规范模式”。它负责处理常规的字符输入输出操作,通常用于终端(tty)设备的基本交互。
-
特点:
- 输入的字符会缓存在内核缓冲区中,直到用户按下回车键(或特定的终止符)后,输入的字符才会传送给应用程序。
- 支持 行编辑 和 特殊字符(如删除、换行、回退等)的处理。
- 实现对字符的 输入模式设置 和 控制字符解释(例如控制字符
Ctrl+C
发送SIGINT
,Ctrl+Z
发送SIGTSTP
)。
-
用途:
- 用于标准的终端或控制台。
- 支持交互式应用程序(如命令行交互)在标准输入输出上的操作。
2. 非规范行规程(non-canonical line discipline, LDISC 1)
与标准行规程不同,非规范行规程通常用于处理流式数据,如串行端口通信或管道中的数据。
-
特点:
- 在非规范模式下,输入的数据不需要等待回车键,数据一旦输入就会立即传输给应用程序。
- 数据是流式的,每个字符的输入都立即可见,不进行缓冲。
- 通常用于实时应用,如串行通信,或者某些高性能的数据流应用。
-
用途:
- 串行设备通信(如
/dev/ttyS0
)。 - 需要高效数据流处理的场景。
- 串行设备通信(如
3. 伪终端行规程(pseudo-terminal line discipline, LDISC 2)
伪终端行规程主要用于处理伪终端设备,通常与图形终端仿真(如 X 终端或虚拟终端)相关。
-
特点:
- 它允许通过伪终端设备来模拟终端交互,包括从控制台发送和接收数据。
- 通常与图形界面的
xterm
、gnome-terminal
或通过 SSH 建立的远程会话等虚拟终端相关联。
-
用途:
- 用于虚拟终端和伪终端设备(如
/dev/pts
)的管理。 - 支持图形终端和多用户会话。
- 用于虚拟终端和伪终端设备(如
4. 串行通信行规程(serial line discipline, LDISC 3)
这是一个用于串行设备的特殊行规程,通常与串行通信(如串行端口、调制解调器)相关。
-
特点:
- 支持串行端口设备的流控制(如 XON/XOFF、RTS/CTS)和特殊字符处理。
- 在串行通信中,对数据的流量控制、错误检测和恢复等操作至关重要。
-
用途:
- 串口通信(如
/dev/ttyS0
)。 - 调制解调器、嵌入式系统的串行端口通信。
- 串口通信(如
5. 网络行规程(network line discipline, LDISC 4)
网络行规程通常用于处理通过网络连接的终端设备,尤其是在需要网络通信时。
-
特点:
- 实现与网络相关的特定数据传输和协议处理功能。
- 常见的应用场景包括使用 PPP(点对点协议)或 SLIP(串行线路互联网协议)连接。
-
用途:
- 支持远程网络设备的输入输出。
- 用于虚拟终端和网络终端之间的数据交换。
Line Discipline 的工作原理
每个 TTY 设备都有一个关联的 Line Discipline,负责处理对终端设备的输入输出操作。在终端设备的整个数据流过程中,Line Discipline 通过一系列的输入处理和输出处理步骤来控制数据的传输行为。
1. 输入处理
当数据输入到终端设备时,Line Discipline 会根据其类型对数据进行相应的处理:
- 在 规范模式 下,输入的字符会缓存在内核的输入缓冲区,直到特定的字符(如换行符)出现时,输入的完整行才会交给应用程序。
- 在 非规范模式 下,字符立即被传输到应用程序,不进行缓冲。
2. 输出处理
Line Discipline 也负责控制从内核输出到终端设备的数据:
- 规范模式:当数据传递给应用程序时,它会自动进行行编辑和控制字符的处理。
- 非规范模式:数据会原样发送到终端设备,适用于实时流数据。
3. 控制字符的处理
Line Discipline 还需要处理控制字符,如:
Ctrl+C
中断当前操作(发送SIGINT
)。Ctrl+Z
挂起当前进程(发送SIGTSTP
)。Ctrl+D
表示输入结束(发送EOF
)。
如何管理 Line Discipline
在 Linux 中,Line Discipline 通过 /dev/tty
设备节点与内核进行交互。管理员可以通过以下命令管理和查看当前的 Line Discipline 配置:
-
查看当前终端的 Line Discipline:
-
cat /proc/tty/driver/serial
-
更改 Line Discipline: 可以使用
stty
命令来配置终端的行为,虽然stty
本身不能直接更改 Line Discipline,但它会影响终端的输入输出模式。
1.4 TTY Drivers以及System Console Drivers
最后,对内核以及驱动工程师来说,更关注的还是具体的TTY设备驱动。在kernel为我们搭建的如此beauty的框架下面,编写相应的driver就成为一件比较简单的事情了。当然的kernel中,主要的TTY driver有两类:
1. 字符设备驱动(Character Device Driver)
字符设备驱动主要负责管理和操作终端设备(如 /dev/tty
、/dev/ttyS0
等)。这些设备主要处理字节流数据,一次处理一个字符。它们包括本地终端、串行端口设备、伪终端等。
主要功能:
- 管理字符输入输出。
- 支持流控制、特殊字符处理等。
- 提供基本的输入输出机制(如读写操作)。
- 实现与内核的字符流交互。
常见的字符设备:
- 本地终端设备(TTY):包括控制台、虚拟终端(
/dev/tty1
,/dev/tty2
,/dev/ttyS0
等)。 - 串行端口设备:如
/dev/ttyS0
,用于串行通信。 - 伪终端设备:如
/dev/pts/0
,用于虚拟终端的连接(如 X 终端和 SSH 会话)。
关键组件:
- tty驱动(tty driver):实现与硬件设备的交互,包括读写字符的操作。
- line discipline(LDISC):控制字符输入输出的行为,如规范模式或非规范模式。
- tty层(TTY layer):负责管理所有的终端设备,协调驱动与应用程序之间的通信。
2. 控制台驱动(Console Driver)
控制台驱动是用于处理终端显示的驱动程序,通常与显示设备(如图形界面控制台或虚拟控制台)相关联。它管理用户与操作系统的交互,输出内核信息,并提供一个基本的界面来显示信息。
主要功能:
- 管理显示输出(如文本输出、日志信息)。
- 提供一个基本的用户交互界面(如
Ctrl+Alt+F1
切换虚拟控制台)。 - 支持文本模式的输入输出。
常见的控制台驱动:
- 本地控制台(如
/dev/console
):通常是与物理显示器和键盘连接的终端设备,用于显示启动信息、内核日志以及与系统交互。 - 虚拟控制台:如
/dev/tty1
,/dev/tty2
等,用于多用户环境下的多终端管理。 - 帧缓冲控制台(Framebuffer Console):支持图形模式的控制台,可以显示图形和文本。
关键组件:
- console driver:实现控制台的输出和输入处理,控制字符的显示格式以及输入的处理。
- framebuffer:提供图形界面的显示支持,允许在控制台上显示图像、字体等。
二、总结
Linux TTY 框架是一个高度模块化的软件架构,负责管理所有与终端相关的操作。TTY(TeleTYpewriter)最初是为字符终端(例如早期的电传打字机)设计的,但随着时间推移,它变得更加通用,支持多种终端类型,如虚拟终端、串行端口、伪终端等。TTY 框架的设计旨在抽象化硬件细节,提供一个一致的接口,允许用户和应用程序与终端设备交互。
2.1、TTY 框架的核心概念
TTY 框架的核心概念主要包括以下几个组成部分:
- TTY设备:表示一个终端设备,可以是本地物理终端、虚拟终端或串行设备。
- TTY驱动:负责与硬件或虚拟终端的通信。
- Line Discipline(LDISC):终端的行级协议,处理输入输出流,支持终端控制(如回显、行模式)。
- 字符设备接口:TTY设备是字符设备,提供了标准的文件操作接口(如
read
、write
、ioctl
)。 - 控制台驱动:提供与内核的交互界面,通常与屏幕输出和用户输入相关。
- 虚拟控制台(VC):虚拟终端是 Linux 系统支持的多个并行终端,允许用户通过多个会话同时操作系统。
2.2、TTY 框架的层次结构
TTY 框架可以从以下几个层次来看:
1. TTY 设备层(TTY Device Layer)
- 这一层表示具体的终端设备,包括串行端口(如
/dev/ttyS0
)和虚拟终端(如/dev/tty1
,/dev/pts/0
)。每个设备都有一个struct tty_struct
,表示一个终端实例。 - 这个结构体中保存了终端的状态、缓冲区、设备驱动程序的引用等信息。
关键函数:
tty_open()
: 打开一个 TTY 设备。tty_read()
和tty_write()
:实现读取和写入操作。tty_ioctl()
:用于处理终端设备特定的控制命令。
2. Line Discipline(LDISC)层
- Line Discipline 是一个层次化的机制,负责处理输入和输出的行级协议。每个 TTY 设备都可以关联一个 Line Discipline 来处理特定的输入输出流。
- Line Discipline 的作用包括字符编辑、流控制、特殊字符的处理(如回车、换行、删除键等)。
- Linux 默认的 Line Discipline 是 N_TTY,但也支持其他协议,如 N_ASKFIRST 或 N_SLIP。
关键函数:
ldisc_open()
: 打开 Line Discipline。ldisc_receive_buf()
: 处理接收到的数据。ldisc_flush()
: 清空 Line Discipline 中的数据缓冲区。
3. TTY 驱动层(TTY Driver Layer)
- TTY 驱动层与硬件或虚拟终端设备进行通信,负责将用户空间的 I/O 请求映射到硬件接口。它包含了各种特定的设备驱动程序,比如串行端口驱动(
ttyS
驱动)、虚拟终端驱动(tty
驱动)等。 - 驱动层的作用是提供对硬件或伪硬件设备的访问,以便从 TTY 设备中读取或写入数据。
关键函数:
tty_driver
结构体:定义了设备驱动的操作(如open
、close
、read
、write
)。tty_insert_flip_char()
: 插入一个字符到 TTY 的缓冲区中。tty_flip_buffer_push()
: 将缓冲区中的字符推送到用户空间。
4. 控制台驱动层(Console Driver Layer)
- 控制台驱动用于处理与控制台设备相关的输入输出,特别是在没有图形界面的情况下。
- 主要负责将内核日志、系统消息显示到用户的控制台,并允许用户与系统进行交互。
- 控制台驱动通常与虚拟控制台和帧缓冲设备相关联。
关键函数:
console_driver
结构体:定义了控制台设备的操作。console_unlock()
:用来打印信息到控制台。console_init()
:初始化控制台设备。
5. 伪终端(PTY)层
- 伪终端是一种特殊的设备,通常用于实现远程会话(如 SSH)或图形化终端(如 X11)。它通过“主/从”终端设备进行通信,其中主设备(
/dev/ptmx
)负责创建和管理虚拟终端对,而从设备(如/dev/pts/0
)则用于和用户交互。 - 伪终端的实现通过对
struct tty_struct
进行分配和管理,允许在用户空间和内核空间之间交换数据。
关键函数:
pty_open()
:打开伪终端设备。tty_init()
:初始化伪终端。
2.3、TTY 框架的主要组件
- tty_struct:每个 TTY 设备都有一个
struct tty_struct
,它包含了终端的状态、输入输出缓冲区、Line Discipline、控制字符等信息。 - tty_driver:每种终端设备类型(如串行、虚拟、伪终端)都有一个对应的
tty_driver
,它定义了对设备的操作函数。 - console_driver:控制台驱动实现,负责内核信息的显示和用户输入的处理。
- tty_port:用于串行端口等硬件设备的表示,支持硬件中断和缓冲区的管理。
2.4、TTY 设备的工作流程
- 设备初始化:当 TTY 设备被打开时,内核会为每个终端分配一个
tty_struct
,并初始化相关的驱动程序、Line Discipline 和缓冲区。 - 数据读写:用户通过
read
或write
系统调用与终端交互,内核将用户数据通过 TTY 驱动程序和 Line Discipline 传输到终端。 - 输入输出处理:TTY 系统通过行编辑(Line Discipline)处理输入的字符流,进行特殊字符的处理,回显或进行流控制。
- 输出到控制台:如果是控制台终端,数据最终将通过控制台驱动显示到屏幕上。