伪终端(pseudoterminal,常缩写为 pty)是操作系统中的一对虚拟字符设备,两端之间构成双向通信通道,其中一端称为主端,另一端称为从端[1]。从端向进程呈现的接口与传统终端一致,因此需要连接终端才能正常工作的程序可以由打开主端的程序驱动[1]。它是远程登录、终端模拟、会话保持和容器交互式访问等功能的底层基础。
定义
从结构上看,伪终端由一对虚拟字符设备构成,两端分别称为主端(master)与从端(slave),二者之间存在一条双向的通信通道[1]。从端提供的接口在行为上等同于一台传统终端,一个原本预期连接终端的进程可以打开从端,随后由打开了主端的程序对它进行控制[1]。
这种机制的价值在于维持程序对终端的假设。不少程序会检查自身是否连接在终端上,并据此决定是否启用交互行为;把标准输入输出接到管道通常会让它们改变做法,而接入伪终端后它们仍可按交互方式运行[2]。向主端写入中断字符(通常为 control-C),会为连接在从端的前台进程组产生 SIGINT 信号;反向地,写入从端的数据可由连接主端的进程读取[1]。
原理
主端与从端之间的数据传递是异步进行的,与物理终端的数据流动相似:写入从端的数据通常较快出现在主端,但未必立即出现;写入主端后,效果在从端可见之前也可能存在少量处理延迟[1]。一种常见的连接关系是,终端模拟器或远程登录服务持有主端,shell 等面向终端的进程连接从端。
flowchart LR
T["终端模拟器或远程登录服务"] --> M["伪终端主端"]
M --> S["伪终端从端"]
S --> P["shell 等面向终端的进程"]
历史上形成了 BSD 与 System V 两套伪终端编程接口,SUSv1 以 System V 接口为基础做了标准化,新编写的程序应当采用该接口[1]。在 Linux 中,System V 风格的实现通常被称为 UNIX 98 伪终端:程序先调用 posix_openpt() 打开主端克隆设备 /dev/ptmx,随后用 grantpt() 修改从端设备的属主和权限、用 unlockpt() 解锁从端,最后以 ptsname() 返回的名称打开对应的从端[1]。
System V 在 STREAMS 设备模型中以驱动程序的形式支持伪终端,并引入了伪终端多路复用器 /dev/ptmx[3]。BSD 风格的伪终端以预先生成的成对设备形式提供,主端与从端分别沿用 pty、tty 前缀的命名,最多只能提供 256 对;进程需要逐个尝试打开主端,直到某次打开成功为止[1][3]。
发展历程
伪终端并非 Unix 独有。DEC 的 PDP-6 分时监控系统至迟在 1967 年就已包含伪终端,用来实现批处理;接替它的 PDP-10 上的 TOPS-10 也在文档中描述了这一设施,PDP-11 的 RSTS/E 以及第三方系统 TENEX 同样具备伪终端[3]。1970 年 4 月的 RFC 46 和 1971 年 1 月 19 日的 RFC 89 中,也可以看到关于伪电传装置以及远程网络控制台接口的记载[4]。
Unix 上的伪终端可追溯到 20 世纪 70 年代末 RAND 与 BBN 为支持经由网络的远程访问而对第 6 版 Unix 所作的修改;现代 Unix 伪终端则产生于 1983 年第八版 Unix 的开发过程,其思路借鉴了 TENEX 中的类似特性,并随后进入 BSD 系统[3]。
BSD 风格伪终端后来被 Unix98 伪终端取代。Unix98 的命名方式不再限制伪终端数量,访问过程也不会带来竞态风险;打开 /dev/ptmx 会返回一个主端文件描述符,并创建与之关联的从端节点 /dev/pts/N[3]。在 Linux 上,自内核 2.6.4 起 BSD 风格伪终端被视为已废弃,可在配置内核时将其关闭,新应用应改用 UNIX 98 伪终端[1]。
标准化方面,伪终端接口自 2004 年(Issue 6)起被单一 UNIX 规范采纳,该规范由 Austin Group 维护;从 2024 年的 Issue 8 开始,规范改用 manager 与 subsidiary 取代原有的 master、slave 表述[3]。微软在 2018 年为 Windows 控制台引入了伪终端接口 ConPTY,使 Windows 上的终端类应用获得与类 Unix 平台类似的机制[5]。
应用
伪终端最主要的用途是提供网络登录服务,SSH、rlogin、telnet 等远程登录程序借助伪终端把远端用户的输入输出接入本机会话[1]。终端模拟器(如 xterm)同样依赖伪终端:它负责与用户交互,把文本输入写入主端供连接在从端的 shell 使用,再从主端读取输出显示给用户,并处理调整屏幕尺寸等终端控制命令[3]。
screen、tmux 等程序在伪终端之上叠加会话上下文,为终端会话提供持久性,用户可以断开与一台计算机的连接,之后再从另一台计算机恢复[3]。script、unbuffer、expect 等工具也以伪终端为工作基础[1]。Python 标准库中的 pty 模块提供 fork、openpty、spawn 等函数,用于以程序方式在另一进程的控制终端中读写数据[2]。
在容器场景中,交互式访问同样依赖伪终端:远程登录容器时,远程登录协议服务程序会自动关联 login 或 shell 程序,向用户提供伪终端从端(PTS)远程伪终端服务[6]。在 Windows 上,ConPTY 允许应用程序托管字符模式程序而不创建宿主窗口,由使用伪控制台的应用程序负责显示输出和收集输入,伪控制台通道中的信息统一采用 UTF-8 编码[7]。
局限
伪终端的数量受系统限制。Linux 内核限制可用的 UNIX 98 伪终端数目:2.6.3 及更早的内核中,该上限在内核编译时确定,允许的最大值为 2048,默认值为 256;自内核 2.6.4 起,上限可通过 /proc/sys/kernel/pty/max 动态调整,/proc/sys/kernel/pty/nr 则记录当前正在使用的数量[1]。BSD 风格的命名方案最多只支持 256 对设备[3]。在 Solaris 上,默认伪终端数量由系统内存量决定,只有在需要限制或增加可同时登录的用户数时才应改动[8]。
查找空闲伪终端也可能带来麻烦。BSD 风格实现中,进程只能逐个尝试打开主端,若不额外采用锁定机制,就可能产生竞态条件[3]。Solaris 的早期版本必须显式配置系统,才能使其具备期望的伪终端数量;从 Solaris 8 发行版起改用新机制,多数情况下不再需要调优[8]。
从数据通道的角度看,伪终端会把进程的标准输出与标准错误合并为同一条流,读取方无法再区分二者的来源[9]。不同平台对伪终端的支持也不一致,例如部分程序库的伪终端功能只支持 macOS 与 Linux,因为其实现依赖类 Unix 的系统调用[9]。
安全方面也存在值得注意之处。当会话与原有会话共享同一终端设备时,针对终端文件描述符的注入类攻击构成风险;su 等程序在启用 --pty 选项时会为会话创建独立的伪终端,使用户不再与原会话共享终端,从而规避 TIOCSTI ioctl 终端注入等攻击[10]。
参见
参考资料
- Ubuntu Manpage: pty . ubuntu.com [引用日期2026-09-29]
- pty --- 伪终端工具 . python.org [引用日期2026-09-29]
- Pseudoterminal . wikipedia.org [引用日期2026-09-29]
- [TUHS] pseudo tty history . tuhs.org [引用日期2026-09-29]
- Software:Pseudoterminal . handwiki.org [引用日期2026-09-29]
- CN108667779B(PDF) . googleapis.com [引用日期2026-09-29]
- 伪consoles – Windows 桌面 - Windows Console . microsoft.com [引用日期2026-09-29]
- 伪终端 - Oracle® Solaris 11.2 可调参数参考手册 . oracle.com [引用日期2026-09-29]
- pty package - github.com/cloudposse/atmos/pkg/terminal/pty . go.dev [引用日期2026-09-29]
- github.com 上的网页 . github.com [引用日期2026-09-29]
浏览次数:0 次
阅读量:0 次 · 阅读完成量:0 次
最近更新:2026-09-29T07:28:21Z
完成率 = 阅读完成量 ÷ 阅读量,分母是阅读量不是浏览次数 —— 关了 JS 的、秒退的都在浏览次数里、不在阅读量里。 详细口径在后台的「数据统计」页。