Linux 偽終端(pty)

通過《Linux 終端(TTY)》一文我們了解到:我們常說的終端分為終端 tty1-6 和偽終端。使用 tty1-6 的情況一般為 Linux 系統直接連了鍵盤和顯示器,或者是使用了 vSphere console 等虛擬化方案,其它情況下使用的都是偽終端。本文將介紹偽終端的基本概念。本文中演示部分使用的環境為 ubuntu 18.04。

偽終端

偽終端(pseudo terminal,有時也被稱為 pty)是指偽終端 master 和偽終端 slave 這一對字符設備。其中的 slave 對應 /dev/pts/ 目錄下的一個文件,而 master 則在內存中標識為一個文件描述符(fd)。偽終端由終端模擬器提供,終端模擬器是一個運行在用戶態的應用程序。

Master 端是更接近用戶顯示器、鍵盤的一端,slave 端是在虛擬終端上運行的 CLI(Command Line Interface,命令行接口)程序。Linux 的偽終端驅動程序,會把 master 端(如鍵盤)寫入的數據轉發給 slave 端供程序輸入,把程序寫入 slave 端的數據轉發給 master 端供(顯示器驅動等)讀取。請參考下面的示意圖(此圖來自互聯網):

我們打開的終端桌面程序,比如 GNOME Terminal,其實是一種終端模擬軟件。當終端模擬軟件運行時,它通過打開 /dev/ptmx 文件創建了一個偽終端的 master 和 slave 對,并讓 shell 運行在 slave 端。當用戶在終端模擬軟件中按下鍵盤按鍵時,它產生字節流并寫入 master 中,shell 進程便可從 slave 中讀取輸入;shell 和它的子程序,將輸出內容寫入 slave 中,由終端模擬軟件負責將字符打印到窗口中。

偽終端的使用場景

偽終端大概有三類使用場景:

  • 像 xterm、gnome-terminal 等圖形界面的終端模擬軟件將鍵盤和鼠標事件轉換為文本輸入,并圖形化地顯示輸出內容
  • 遠程 shell 應用程序(如 sshd)在客戶機上的遠程終端和服務器上的偽終端之間中繼輸入和輸出
  • 多路復用器應用,如 screen 和 tmux。它們把輸入和輸出從一個終端轉播到另一個終端,使文本模式的應用程序從實際的終端上脫離

Linux 中為什么要提出偽終端這個概念呢?shell 等命令行程序不可以直接從顯示器和鍵盤讀取數據嗎?
為了同屏運行多個終端模擬器、并實現遠程登錄,還真不能讓 shell 直接跨過偽終端這一層。在操作系統的一大思想——虛擬化的指導下,為多個終端模擬器、遠程用戶分配多個虛擬的終端是有必要的。上圖中的 shell 使用的 slave 端就是一個虛擬化的終端。Master 端是模擬用戶一端的交互。之所以稱為虛擬化的終端,是因為它除了轉發數據流外,還要有點終端的樣子。

偽終端原理

偽終端本質上是運行在用戶態的終端模擬器創建的一對字符設備。其中的 slave 對應 /dev/pts/ 目錄下的一個文件,而 master 則在內存中標識為一個文件描述符(fd)。對于偽終端來說,重點是軟件仿真終端程序運行在用戶空間,這是它與終端的本質區別,請參考下面的示意圖:

/dev/ptmx 是一個字符設備文件,當進程打開 /dev/ptmx 文件時,進程會同時獲得一個指向 pseudoterminal master(ptm)的文件描述符和一個在 /dev/pts 目錄中創建的 pseudoterminal slave(pts) 設備。通過打開 /dev/ptmx 文件獲得的每個文件描述符都是一個獨立的 ptm,它有自己關聯的 pts,ptmx(可以認為內存中有一個 ptmx 對象)在內部會維護該文件描述符和 pts 的對應關系,對這個文件描述符的讀寫都會被 ptmx 轉發到對應的 pts。我們可以通過 lsof 命令查看 ptmx 打開的文件描述符:

$ sudo lsof /dev/ptmx

進程默認的 IO

一般情況下我們通過遠程連接的方式執行命令時,進程的標準輸入、標準輸出和標準錯誤輸出都會綁定到偽終端上,下面是一個簡單的 demo 程序:

#include <stdio.h>
#include <unistd.h>
int main()
{
    printf("PID : %d\n", getpid());
    sleep(200);

    printf("\n");
    return 0;
}

把這段代碼保存在文件 mydemo.c 中,然后執行下面的命令編譯并執行該程序:

$ gcc -Wall mydemo.c -o demo
$ ./demo

demo 程序輸出了自己進程的 PID,現在另外開一個終端執行 lsof 命令:

$ lsof -p 17981

可以看到進程的 0u(標準輸入)、1u(標準輸出)、2u(標準錯誤輸出)都綁定到了偽終端 /dev/pts/0 上。

 

參考:
Linux TTY/PTS概述
The TTY demystified
偽終端 pts man page
偽終端 pty man page

posted @ 2019-09-29 08:45 sparkdev 閱讀(...) 評論(...) 編輯 收藏
乐就娱乐