簡體   English   中英

你可以用 pty 做什么?

[英]What can you do with a pty?

閱讀了各種資源,包括http://www.linusakesson.net/programming/tty/我仍然對偽終端的結構和使用感到困惑和好奇在 linux 終端(bash 不是 tty)中,我們有三個流:

  • 標准輸入
  • 標准輸出
  • 標准錯誤

每個都有一個文件描述符。 這些文件描述符 map 到流 FILE*。 例如,我們可以使用 fileno() 和 fdopen() 在它們之間切換。

我可以用 pty 做什么?

我通常認為 stdout、stdin 和 stderr 與進程相關聯。 我認為說存在與 pty 關聯的標准輸出、標准輸入和標准錯誤是不正確的。 那么給定一個 pty(對於像 bash 這樣的進程,而不是它的 pid),你如何獲得它們並將它們連接到 bash 意義上的終端?

如何將單個文件描述符(對於pty)map 分配給三個流? (顯然不是)它能以某種方式將 map 發送到進程嗎?

使用 openpty() 時,我認為要連接主從終端上的進程,您需要為 stdin、stdout 和 stderr 設置單獨的管道,並使用 select 循環連接它們並在它們之間傳輸數據。

但是您是否可以假設 stdin 和 stdout 已連接(通過 tty)並設置一個 stderr pipe 以避免 stdout 和 stderr 被合並?

如果我們有一個主從進程,“額外的”tty 實際上為我們做了什么?


以下是一些我認為我知道並且相關的事情:

openpty() function 返回一對文件描述符,表示偽終端的主從端。

每個文件描述符都指向字符設備——參見https://man7.org/linux/man-pages/man7/pty.7.html

現在 Unix 中的設備只是特殊文件,因此文件描述符很有意義。

bash 終端有三個流,但 tty 只是一個通道(stdout 和 stderr 之間沒有區別)。 虛擬終端 (tty) 也有一些“魔力”(例如,線路規則)。

如果我要問 FD 為 pty map 做什么文件,它將是設備文件 /dev/ptyX 或其他什么。

在由 openpty() 創建的主/從通道的兩端都有一個“終端”。

它實際傳輸什么數據?

我通常將文件描述符視為代表文件的 kernel 結構表的索引。 顯然,其中一些文件是流(FILE* 的 kernel 版本),其中一些是特殊的。

與終端 (tty) 關聯的主要用戶空間結構是使用 tcgetattr 從 FD 獲得的 termios。 這允許您為終端設置參數,但它不識別任何流或進程或線路規程。

對於 FILE* 或您認為是文件的 fd,有一組非常清晰且被廣泛使用的 API 定義了您可以做什么。

我隱約知道我可以用字符設備做什么(例如https://unix.stackexchange.com/questions/37829/how-do-character-device-or-character-special-files-work )。

但是你可以用 pty 做什么?

給定一個 stream,我們可以使用 isatty() 確定它是否有關聯的 tty,並使用 ttyname() 識別 tty。 所以在內部某個地方有一些東西知道 tty 和 stream 是相關聯的。 isatty(STDERR_FILENO) 也是真的,所以它不僅僅是標准輸入和標准輸出。


最初引起我興趣的是一個程序,它應該是一個守護進程,但創建了一個 ncurses UI。 我一直在考慮如何讓進程按需 UI(在 pty 中)打開其 ncurses。 screen 程序允許您執行此操作,但它在幕后做了什么。

描述此用例的一個問題是 -將終端附加到作為守護進程運行的進程(以運行 ncurses UI)


另一種看待這個問題的方式是從面向 object 的角度來看。

有哪些函數以“pty”作為參數。 它們的用途是什么? 如果我有一個 pty object。它有什么方法?

如果您只是列出這些函數,那么手冊頁中會遺漏一些關鍵信息。 什么是前置條件、后置條件和不變量。

文件描述符是每個進程的標識符,指的是內核內部文件描述。 文件描述包括常規文件的 position,以及打開它是為了讀取、寫入、兩者還是僅用於路徑操作。 這些在fcntl() 手冊頁中稱為文件狀態標志。 每個文件描述符都有自己的描述符標志(目前只有一個,close-on-exec,O_CLOEXEC/FD_CLOEXEC)。

FILE 流是標准的 C 庫抽象。 如果我們忽略 GNU fopencookie() 擴展,所有 C 流都只與一個文件描述符相關聯,並且只有一個 C stream 可以與給定的文件描述符相關聯。 (當然,您可以復制文件描述符,並使用 fdopen() 將 FILE stream 與新的復制文件描述符相關聯。但是,C 庫不知道這兩個獨立的流現在共享基礎文件 position,這可以當描述符引用文件時會導致問題。當描述符引用設備時,比如終端或偽終端,並且原始描述符以兼容模式(讀/寫/兩者)打開,則沒有問題。)

偽終端基本上是一個雙向 pipe,中間有 kernel termios 層。 在偽終端中運行的典型進程具有引用相同文件描述的所有三個標准描述符:偽終端的從端。

termios層不只是對slave端讀寫的數據做一些小的處理:它還可以引起信號的提升。 每個偽終端還與一個大小(行數和列數,以字符為單位)相關聯。 當偽終端的主端改變大小時,前台進程組(下面解釋)中的每個進程都會收到一個 SIGWINCH 信號。 如果主機關閉偽終端,所有將其作為控制終端的進程都將收到 SIGHUP 信號。 (這也是為什么/如何在您運行命令或程序時關閉終端 window 通常會終止該命令或程序。)

每個進程最多可以有一個控制終端。 每個終端最多可以與一個前台進程組相關聯(通過tcgetpgrp() 和 tcsetpgrp()管理)。 sessiongetsid()setsid() 、前台進程組、后台進程組的概念,都涉及終端(或偽終端)。

當進程將所有三個標准 C FILE 流連接到終端或偽終端時,標准輸入從主機接收數據(通過 termios 層),標准 output 和標准錯誤都將數據發送到主機(通過 termios 層)。 所以,標准輸入、output、error之間並沒有直接聯系,只是連接到同一個終端/偽終端slave端。


假設您是 GTK+3 C 程序員,並且您想要創建自己的終端 window 應用程序,例如 xterm 或 gnome-terminal。

應用程序將為每個 window 創建一個偽終端。每當它收到鍵盤事件時,它都會將相應的鍵序列( 1表示1 :表示: ,等等)寫入主偽終端描述符。 它將從主偽終端描述符中讀取的所有內容都將呈現在 window 中。對於從屬端,應用程序派生一個子進程,為所有三個標准流打開從屬端,並執行/bin/login -f $(id -un)具有 root 權限,或者對當前用戶本身執行等效操作。 本質上,為用戶完成了一些內務處理,最后在用戶的主目錄中為用戶打開了一個登錄名 shell。

大多數終端仿真器支持ANSI 轉義碼和 xterm 擴展。 終端信息數據庫和 ncurses 應用程序依賴於將 TERM 環境變量設置為當前終端類型。 (xterm-256color 可能是 Linux 中最常見的,但也支持其他顏色。終端仿真器應該在從屬端啟動進程時設置正確的 TERM 環境變量;本身沒有自動檢測。)

您需要偽終端的所有事情都涉及類似終端的環境。 它可能面對人類用戶(如上面的 GUI 終端應用程序),或者它可能面對像 nano、vim、emacs、links 或 lynx 這樣的應用程序,這些應用程序在終端中運行,並代表用戶執行操作。

screen的工作方式是,即使用戶未連接到該 screen 進程,它也會維護應用程序在其中運行的偽終端。 當用戶想要分離(即斷開連接但讓進程繼續運行)時,屏幕應用程序(它在其下運行進程的偽終端的主控端)從用戶正在使用的終端/偽終端分離,但繼續運行。 要恢復, screen -r命令(以及已經運行 screen 時的鍵盤快捷鍵)將用戶終端或偽終端重新連接到 screen 應用程序。

本質上,使用這樣的終端多路復用器,您擁有連接到多路復用器應用程序的真實用戶終端或偽終端(可以是終端仿真器,如 xterm 或 gnome-terminal,甚至是非終端進程,如與本機的遠程 SSH 連接) ,當連接到偽終端(多路復用器應用程序是其主控程序)時,它會中繼按鍵和輸出,shell 或在該偽終端下運行的其他應用程序。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM