Linux 偽終端(pty)
通過(guò)《Linux 終端(TTY)》一文我們了解到:我們常說(shuō)的終端分為終端 tty1-6 和偽終端。使用 tty1-6 的情況一般為 Linux 系統(tǒng)直接連了鍵盤和顯示器,或者是使用了 vSphere console 等虛擬化方案,其它情況下使用的都是偽終端。本文將介紹偽終端的基本概念。本文中演示部分使用的環(huán)境為 ubuntu 18.04。
偽終端
偽終端(pseudo terminal,有時(shí)也被稱為 pty)是指?jìng)谓K端 master 和偽終端 slave 這一對(duì)字符設(shè)備。其中的 slave 對(duì)應(yīng) /dev/pts/ 目錄下的一個(gè)文件,而 master 則在內(nèi)存中標(biāo)識(shí)為一個(gè)文件描述符(fd)。偽終端由終端模擬器提供,終端模擬器是一個(gè)運(yùn)行在用戶態(tài)的應(yīng)用程序。
Master 端是更接近用戶顯示器、鍵盤的一端,slave 端是在虛擬終端上運(yùn)行的 CLI(Command Line Interface,命令行接口)程序。Linux 的偽終端驅(qū)動(dòng)程序,會(huì)把 master 端(如鍵盤)寫入的數(shù)據(jù)轉(zhuǎn)發(fā)給 slave 端供程序輸入,把程序?qū)懭?slave 端的數(shù)據(jù)轉(zhuǎn)發(fā)給 master 端供(顯示器驅(qū)動(dòng)等)讀取。請(qǐng)參考下面的示意圖(此圖來(lái)自互聯(lián)網(wǎng)):

我們打開的終端桌面程序,比如 GNOME Terminal,其實(shí)是一種終端模擬軟件。當(dāng)終端模擬軟件運(yùn)行時(shí),它通過(guò)打開 /dev/ptmx 文件創(chuàng)建了一個(gè)偽終端的 master 和 slave 對(duì),并讓 shell 運(yùn)行在 slave 端。當(dāng)用戶在終端模擬軟件中按下鍵盤按鍵時(shí),它產(chǎn)生字節(jié)流并寫入 master 中,shell 進(jìn)程便可從 slave 中讀取輸入;shell 和它的子程序,將輸出內(nèi)容寫入 slave 中,由終端模擬軟件負(fù)責(zé)將字符打印到窗口中。
偽終端的使用場(chǎng)景
偽終端大概有三類使用場(chǎng)景:
- 像 xterm、gnome-terminal 等圖形界面的終端模擬軟件將鍵盤和鼠標(biāo)事件轉(zhuǎn)換為文本輸入,并圖形化地顯示輸出內(nèi)容
- 遠(yuǎn)程 shell 應(yīng)用程序(如 sshd)在客戶機(jī)上的遠(yuǎn)程終端和服務(wù)器上的偽終端之間中繼輸入和輸出
- 多路復(fù)用器應(yīng)用,如 screen 和 tmux。它們把輸入和輸出從一個(gè)終端轉(zhuǎn)播到另一個(gè)終端,使文本模式的應(yīng)用程序從實(shí)際的終端上脫離
Linux 中為什么要提出偽終端這個(gè)概念呢?shell 等命令行程序不可以直接從顯示器和鍵盤讀取數(shù)據(jù)嗎?
為了同屏運(yùn)行多個(gè)終端模擬器、并實(shí)現(xiàn)遠(yuǎn)程登錄,還真不能讓 shell 直接跨過(guò)偽終端這一層。在操作系統(tǒng)的一大思想——虛擬化的指導(dǎo)下,為多個(gè)終端模擬器、遠(yuǎn)程用戶分配多個(gè)虛擬的終端是有必要的。上圖中的 shell 使用的 slave 端就是一個(gè)虛擬化的終端。Master 端是模擬用戶一端的交互。之所以稱為虛擬化的終端,是因?yàn)樗宿D(zhuǎn)發(fā)數(shù)據(jù)流外,還要有點(diǎn)終端的樣子。
偽終端原理
偽終端本質(zhì)上是運(yùn)行在用戶態(tài)的終端模擬器創(chuàng)建的一對(duì)字符設(shè)備。其中的 slave 對(duì)應(yīng) /dev/pts/ 目錄下的一個(gè)文件,而 master 則在內(nèi)存中標(biāo)識(shí)為一個(gè)文件描述符(fd)。對(duì)于偽終端來(lái)說(shuō),重點(diǎn)是軟件仿真終端程序運(yùn)行在用戶空間,這是它與終端的本質(zhì)區(qū)別,請(qǐng)參考下面的示意圖:

/dev/ptmx 是一個(gè)字符設(shè)備文件,當(dāng)進(jìn)程打開 /dev/ptmx 文件時(shí),進(jìn)程會(huì)同時(shí)獲得一個(gè)指向 pseudoterminal master(ptm)的文件描述符和一個(gè)在 /dev/pts 目錄中創(chuàng)建的 pseudoterminal slave(pts) 設(shè)備。通過(guò)打開 /dev/ptmx 文件獲得的每個(gè)文件描述符都是一個(gè)獨(dú)立的 ptm,它有自己關(guān)聯(lián)的 pts,ptmx(可以認(rèn)為內(nèi)存中有一個(gè) ptmx 對(duì)象)在內(nèi)部會(huì)維護(hù)該文件描述符和 pts 的對(duì)應(yīng)關(guān)系,對(duì)這個(gè)文件描述符的讀寫都會(huì)被 ptmx 轉(zhuǎn)發(fā)到對(duì)應(yīng)的 pts。我們可以通過(guò) lsof 命令查看 ptmx 打開的文件描述符:
$ sudo lsof /dev/ptmx

進(jìn)程默認(rèn)的 IO
一般情況下我們通過(guò)遠(yuǎn)程連接的方式執(zhí)行命令時(shí),進(jìn)程的標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤輸出都會(huì)綁定到偽終端上,下面是一個(gè)簡(jiǎn)單的 demo 程序:
#include <stdio.h> #include <unistd.h> int main() { printf("PID : %d\n", getpid()); sleep(200); printf("\n"); return 0; }
把這段代碼保存在文件 mydemo.c 中,然后執(zhí)行下面的命令編譯并執(zhí)行該程序:
$ gcc -Wall mydemo.c -o demo $ ./demo

demo 程序輸出了自己進(jìn)程的 PID,現(xiàn)在另外開一個(gè)終端執(zhí)行 lsof 命令:
$ lsof -p 17981

可以看到進(jìn)程的 0u(標(biāo)準(zhǔn)輸入)、1u(標(biāo)準(zhǔn)輸出)、2u(標(biāo)準(zhǔn)錯(cuò)誤輸出)都綁定到了偽終端 /dev/pts/0 上。
參考:
Linux TTY/PTS概述
The TTY demystified
偽終端 pts man page
偽終端 pty man page

浙公網(wǎng)安備 33010602011771號(hào)