基于 Linux 4.14,iMX6ULL 配置编译。分析
drivers/tty/下各源码文件的职责,并梳理 TTY 子系统的分层结构。
一、TTY 子系统概述
TTY(Teletype)子系统负责管理 Linux 中所有的”终端类”设备,覆盖范围很广:
| 设备类型 | 设备节点 | 说明 |
|---|---|---|
| 串口(UART) | /dev/ttymxc0、/dev/ttyS0 | 嵌入式最常用,调试打印、Shell 登录 |
| 虚拟终端 | /dev/tty1~/dev/tty6 | 桌面 Linux 的文字控制台(Ctrl+Alt+F1 切换) |
| 伪终端 | /dev/pts/0、/dev/ptmx | SSH、终端模拟器(xterm、tmux)使用 |
/dev/ 下对应的设备节点:
# 串口设备(iMX6ULL 上的 UART)
crw-rw---- root dialout 207, 16 /dev/ttymxc0
crw-rw---- root dialout 207, 17 /dev/ttymxc1
# 虚拟终端(桌面 Linux)
crw--w---- root tty 4, 1 /dev/tty1
crw--w---- root tty 4, 2 /dev/tty2
# 伪终端
crw-rw-rw- root tty 5, 2 /dev/ptmx ← 主端,open 后动态创建从端
crw--w---- root tty 136, 0 /dev/pts/0 ← 从端(SSH、tmux 等使用)
二、分层架构
TTY 子系统由上到下分为四层:
┌─────────────────────────────────────────┐
│ 用户空间进程 │
│ open/read/write/ioctl /dev/ttymxc0 │
└───────────────────┬─────────────────────┘
│ 系统调用
┌───────────────────▼─────────────────────┐
│ VFS 文件操作层 │
│ tty_io.c(tty_fops) │
└───────────────────┬─────────────────────┘
│
┌───────────────────▼─────────────────────┐
│ 行规程(Line Discipline) │
│ n_tty.c(默认 N_TTY) │
│ 处理:规范模式/原始模式、回显、Ctrl+C │
└───────────────────┬─────────────────────┘
│
┌───────────────────▼─────────────────────┐
│ UART 子系统核心 │
│ serial_core.c │
└───────────────────┬─────────────────────┘
│
┌───────────────────▼─────────────────────┐
│ UART Host 驱动 │
│ imx.c / fsl_lpuart.c(操作寄存器) │
└───────────────────┬─────────────────────┘
│
硬件 UART 控制器
嵌入式串口数据流(接收方向):
硬件 UART 收到数据
│ 触发中断
▼
imx.c 中断处理函数
│ 调用 tty_insert_flip_char()
▼
tty_buffer.c 翻转缓冲区(flip buffer)
│ 调度 workqueue,脱离中断上下文
▼
n_tty.c 行规程处理
│ 规范模式:行缓冲 + 回显;原始模式:直接转发
▼
tty_io.c 的 tty_read()
│ 唤醒阻塞的 read() 调用
▼
用户空间 read() 返回数据
三、源码文件总览
以 iMX6ULL 配置编译后,drivers/tty/ 生成的主要文件,按功能分组:
drivers/tty/
├── TTY 核心层
│ ├── tty_io.c ← 最核心,file_operations 实现
│ ├── tty_ldisc.c ← 行规程注册与切换管理
│ ├── tty_buffer.c ← 翻转缓冲区,中断/进程上下文桥梁
│ ├── tty_port.c ← 串口助手结构(tty_port)
│ ├── tty_ioctl.c ← IOCTL 接口(TCGETS/TCSETS 等)
│ ├── tty_baudrate.c ← 波特率转换表
│ ├── tty_jobctrl.c ← 作业控制(前台/后台进程组、SIGHUP)
│ ├── tty_mutex.c ← TTY 互斥锁
│ └── tty_ldsem.c ← 行规程切换专用读写信号量
│
├── 行规程
│ ├── n_tty.c ← 默认行规程 N_TTY(规范/原始模式)
│ └── n_null.c ← 空行规程 N_NULL(丢弃所有数据)
│
├── 串口/UART 层(嵌入式核心)
│ └── serial/
│ ├── serial_core.c ← UART 子系统框架
│ ├── imx.c ← iMX 系列 UART Host 驱动
│ ├── fsl_lpuart.c ← Freescale LPUART Host 驱动
│ └── serial_mctrl_gpio.c ← GPIO 模拟硬件流控信号
│
├── 早期控制台
│ └── earlycon.c ← 内核早期启动打印,完整驱动加载前使用
│
├── 虚拟终端(桌面 Linux,嵌入式通常不涉及)
│ └── vt/
│ ├── vt.c ← 虚拟终端核心(tty1~tty6)
│ ├── vt_ioctl.c ← 虚拟终端 IOCTL
│ ├── vc_screen.c ← 屏幕缓冲区,提供 /dev/vcs*
│ ├── keyboard.c ← 键盘输入映射
│ ├── defkeymap.c ← 默认键盘映射表数据
│ ├── consolemap.c ← 字符编码映射(ASCII/Unicode 转换)
│ ├── consolemap_deftbl.c ← 默认字符映射表数据
│ └── selection.c ← 虚拟终端文本复制粘贴
│
└── 特殊功能
├── pty.c ← 伪终端(SSH/terminal emulator 使用)
└── sysrq.c ← Magic SysRq(Alt+SysRq+key 调试组合键)
四、各文件详解
4.1 TTY 核心层
tty_io.c
TTY 子系统最核心的文件。实现 /dev/ttyX 设备文件的 file_operations(即 tty_fops),包括 tty_open()、tty_read()、tty_write()、tty_release(),是 VFS 与下层行规程之间的桥梁。
tty_ldisc.c
行规程(Line Discipline)的注册和管理。行规程是 TTY 核心层与驱动层之间的处理模块,定义了数据的加工方式(回显、特殊字符处理、缓冲策略等)。提供 tty_ldisc_register()、tty_set_ldisc() 等接口,支持运行时切换。
tty_buffer.c
翻转缓冲区(flip buffer)管理。UART 中断处理函数将接收到的数据放入 flip buffer,然后调度 workqueue 在进程上下文中处理,实现中断上下文与行规程处理的解耦,是高速串口不丢数据的关键机制。
tty_port.c
提供 struct tty_port 辅助结构,封装了串口打开/关闭的引用计数、阻塞打开(等待载波检测 DCD)、唤醒等通用逻辑,减少各 UART Host 驱动的重复代码。
tty_ioctl.c
实现 TTY 设备的 ioctl 接口,处理 POSIX 标准的终端控制命令:
| 常用 IOCTL | 说明 |
|---|---|
TCGETS / TCSETS | 获取/设置终端参数(struct termios) |
TIOCGWINSZ | 获取终端窗口大小 |
TIOCMGET / TIOCMSET | 获取/设置调制解调器控制信号(RTS/CTS 等) |
TCSBRK | 发送 BREAK 信号 |
tty_baudrate.c
波特率转换表,将 POSIX 波特率常量(B9600、B115200 等)映射为实际数值:
static const speed_t baud_table[] = {
0, 50, 75, 110, ..., 115200, 230400, 460800,
500000, 576000, 921600, 1000000, ..., 4000000
};
注意:最高支持 4 Mbps。超过 4M 的波特率无法通过 stty 设置,需要通过 ioctl 直接操作,或修改该表。
tty_jobctrl.c
作业控制(Job Control)实现。管理进程组、会话与控制终端的关系:当 Shell 的控制终端关闭时向前台进程组发 SIGHUP;处理 Ctrl+C(SIGINT)、Ctrl+Z(SIGTSTP)等信号;实现 tcgetpgrp() / tcsetpgrp()。
tty_mutex.c
TTY 子系统全局互斥锁封装,保护对 TTY 数据结构的并发访问。
tty_ldsem.c
行规程读写信号量(ldsem = line discipline semaphore)。不是行编辑或信号处理功能,是一个专用同步原语:切换行规程时必须等待所有正在进行的读写操作完成,同时阻止新的读写进入,tty_ldsem 专门实现这个精确的同步语义。
4.2 行规程
n_tty.c
N_TTY 行规程(编号 0),是所有串口打开后的默认行规程。实现两种工作模式:
| 模式 | 特点 | 典型用途 |
|---|---|---|
| 规范模式(canonical) | 以行为单位缓冲;支持退格、删除等行编辑;Ctrl+C 发 SIGINT;Enter 后才交给应用 | Shell 交互 |
| 原始模式(raw) | 字节直通,不做任何处理;read() 可立即返回单字节 | 串口透传、自定义协议 |
切换模式通过 tcsetattr() 修改 termios 的 c_lflag 实现。
n_null.c
N_NULL 行规程。与 /dev/null 完全无关,是一个丢弃所有数据的空行规程:向其写入的数据直接丢弃,读取永远返回 0 字节。在需要将 TTY 切换为”静默”状态时使用(/dev/null 的实现在 drivers/char/mem.c)。
4.3 串口 / UART 层
serial_core.c
UART 子系统的核心框架,相当于串口驱动的”核心层”(类比 PWM 的 core.c):
- 提供
uart_register_driver()/uart_add_one_port(),供 Host 驱动注册 - 实现串口的
tty_operations,对接 TTY 核心层 - 统一处理波特率配置、流控、字符发送等通用逻辑
- 管理
/dev/ttymxc*、/dev/ttyS*等设备节点的创建
imx.c
iMX 系列 UART 的 Host 驱动,实现 uart_ops:
probe():初始化寄存器、申请中断、注册到 serial_core- 中断处理:接收时调用
tty_insert_flip_char()写入 flip buffer;发送时读取队列写寄存器 set_termios():根据termios参数配置波特率、数据位、停止位、校验位等寄存器
fsl_lpuart.c
Freescale LPUART(Low Power UART)Host 驱动,结构与 imx.c 类似但寄存器定义不同,适用于某些 iMX7/iMX8 变体和 Kinetis 系列芯片。
serial_mctrl_gpio.c
允许用 GPIO 引脚模拟串口硬件流控信号(RTS/CTS/DTR/DSR/DCD/RI)。当硬件 UART 控制器没有专用流控引脚时,可以用普通 GPIO 代替,由该文件提供统一接口。
earlycon.c
早期控制台支持。在内核启动极早期(start_kernel 执行之前),完整的 UART 驱动尚未初始化,earlycon 提供最小化的串口输出能力,用于打印极早期的启动日志。通过 cmdline 参数激活,例如:
earlycon=imx-uart,0x02020000,115200
4.4 虚拟终端
以下文件主要用于桌面 Linux 的文字控制台(
/dev/tty1~/dev/tty6),嵌入式开发通常不涉及,了解职责即可。
| 文件 | 职责 |
|---|---|
vt.c | 虚拟终端核心,管理多个文字控制台,处理 VT100/ANSI 转义序列 |
vt_ioctl.c | 虚拟终端 IOCTL(切换 VT、设置模式等) |
vc_screen.c | 屏幕缓冲区管理,提供 /dev/vcs* 设备接口 |
keyboard.c | 键盘输入:将扫描码翻译为字符/功能键,处理修饰键状态 |
defkeymap.c | keyboard.c 使用的默认键盘映射表(由 keymaps 文件生成) |
consolemap.c | 控制台字符编码映射,支持不同字符集与 Unicode 的转换 |
consolemap_deftbl.c | consolemap.c 使用的默认映射表数据 |
selection.c | 虚拟终端文本选择与粘贴(配合 GPM 鼠标守护进程) |
4.5 特殊功能
pty.c
伪终端(Pseudo Terminal)实现。SSH、tmux、xterm 等都依赖伪终端:
sshd 进程
│ open("/dev/ptmx") → 获得主端 fd
│ grantpt/unlockpt/ptsname → 获得从端路径 /dev/pts/N
│
├─ 主端(ptmx fd) ← sshd 读写,转发给 SSH 客户端
└─ 从端(/dev/pts/N)← 远端 Shell 的控制终端
sysrq.c
Magic SysRq 支持(Alt+SysRq+key)。注册了一组内核调试命令:b(reboot)、s(sync)、u(remount ro)、t(show tasks)等。串口调试时可通过发送特定字符序列触发(需开启内核配置 CONFIG_MAGIC_SYSRQ)。
五、嵌入式开发关注重点
对于嵌入式串口开发,各文件重要性排序:
| 优先级 | 文件 | 原因 |
|---|---|---|
| ★★★ | imx.c / fsl_lpuart.c | Host 驱动,寄存器操作,probe 实现,移植时主要修改这里 |
| ★★★ | serial_core.c | UART 框架,理解驱动注册流程和 tty_operations |
| ★★☆ | n_tty.c | 行规程,理解规范模式/原始模式、回显、特殊字符处理 |
| ★★☆ | tty_buffer.c | flip buffer,理解中断接收的数据流转,排查丢数据问题 |
| ★☆☆ | tty_io.c | 理解 read/write 系统调用的完整路径 |
| ★☆☆ | earlycon.c | 移植新平台时,配置早期串口打印 |
虚拟终端相关文件(vt.c、keyboard.c 等)在纯嵌入式场景下基本不需要深入了解。
关联笔记
- 平台设备驱动模型 —
imx.c的 probe/remove 遵循平台设备驱动框架 - 芯片上电到Linux启动全流程 — earlycon 在启动流程中的位置(start_kernel 之前的极早期阶段)
延伸阅读
TTY 位于用户空间与串口驱动之间,建议结合启动流程和设备模型理解。
- 返回嵌入式 Linux 学习路线,确认本文在完整知识结构中的位置。
- 芯片上电到 Linux 启动全流程
- Linux Platform 设备驱动模型




