當你開啟終端並輸入命令時會發生什麼?(上)

2023-12-13 21:00:40

哈嘍大家好,我是鹹魚

參加過校招面試的小夥伴們肯定對下面這道面試題很熟悉:「當你在瀏覽器輸入一段網址後會發生什麼?」。這道面試題可以說是很經典了,因為其涉及大量網路協定,可以非常直觀的看出小夥伴們對計算機網路體系的整體把握程度

但如果問題換成:「當你開啟終端並輸入 ls 時會發生什麼?」,有多少小夥伴能夠回答出來呢?

終端的前世今生

大多數現代終端應用程式的工作方式都來自於其歷史前輩——電傳打字機(teletypes,簡稱 tty)

在大型計算機的時代,當時資料儲存在磁帶上,計算機的記憶體以 kB 為單位,電傳打字機就是為了它們而被設計出來

如上圖,左邊的是 IBM 2741電傳打字機,右邊是 IBM System/360 Mo. 40大型計算機

電傳打字機是允許使用者與計算機互動的基本文字使用者端。teletypes 其實是 teletypewriter的縮寫,因為它是從打字機(typewriters)演變過來的

如上圖所示,電傳打字機和大型計算機通過連線兩端的物理線來進行通訊。溝通過程如下:

  • 當用戶從電傳打字機輸入時,ASCII 文字將一個字元一個字元地通過網路傳輸
  • 計算機的核心接收字元並對其進行解碼
  • 接著字元被送到一個名為 TTY driver 的驅動程式,這裡負責將輸入傳送到使用者程式並收集輸出
  • 最後,核心將輸出傳送回電傳打字機 ,以便顯示給使用者

需要提到的一點是 line discipline(行規則),它會將字元緩衝到核心記憶體中,直到按下 Enter」 鍵,程式才會接收到輸入

line discipline 允許這塊緩衝區是可編輯的,並提供了一些與程式無關的快捷鍵(例如 ctrl-w)

這在當時是一項重要的效能優化,因為讓程式設計師一個字元一個字元的處理是非常低效的

隨著計算技術的進步,這些獨立元件中的許多都實現了現代化。比如說電傳打字機被終端所取代,終端是完全電子的機器,包括電子顯示器

上圖是 DEC 於 1978 年釋出的 VT100 終端機(VT = video terminal),它實現並推廣了至今仍在使用的 ANSI 跳脫碼

隨著電子終端的誕生,出現了越來越多的功能(例如顏色、鈴聲)。但本質上跟電傳打字機完全相同——傳送輸入字元流並顯示輸出

現如今人人都有一臺自己的電腦,這些電腦的作業系統可以監督許多應用程式,終端不再是專門的硬體,而是變成了這些應用程式中的一個

與典型的 GUI 應用程式一樣,終端是作業系統監督下的一個程序,它監聽來自使用者的事件和輸入,並告訴作業系統在視窗中顯示什麼(終端不直接與外設互動,而是通過驅動程式和視窗管理器)

有時候我們還會聽到 」終端模擬器「 這個詞,而不是簡單的稱之為 」終端「。這是因為 」終端「 指的是專門的硬體(終端機),而現在大多數的終端只是對該裝置的模擬,是一個應用程式

但是我們這裡不做區分,」終端模擬器「 和 」終端「 含義一樣

那麼當我們開啟終端時會發生什麼呢?

開啟終端

上面我們提到過,終端是一個應用程式,能夠讓你 」使用你的電腦「(即在上面執行程式)。我們的電腦上可能已經存在了 ls、rm、mv 等程式

但是我們不滿足於使用這些簡單的命令,我們還希望使用指令碼來實現自動化, 這些指令碼將許多命令的序列組合在一起,使用分支條件邏輯,執行重複迴圈或並行化命令等

為了讓計算機能夠讀懂我們的指令碼並執行起來,我們需要一個完整的可互動的解釋型的程式設計環境——shell

將其他程式作為程序執行,讓作業系統核心讀懂你寫的指令碼,這些工作都由 shell 完成。目前常見的 shell 有 Bash、Zsh 等

終端和 shell 是兩個獨立的程式:

  • shell 負責解釋你輸入的命令
  • 終端負責 UI 相關的東西,比如字型、顏色等

當我們開啟終端時,終端會根據使用者生成一個 shell 程序,以及使用者與 shell 之間,使用者與 shell 啟動的程序之間通訊的方法

這個 shell 程序負責解釋和執行使用者輸入的命令,並與使用者進行互動。使用者在終端輸入的命令將通過這個通訊通道傳遞給 shell 程序進行解釋執行,並將執行結果反饋給使用者顯示在終端上

建立 PTY

偽終端裝置(PTY)是在計算機作業系統中建立的一個虛擬裝置,用於模擬物理終端的功能

在 UNIX、Linux 和類 UNIX 系統中,PTY 用於在使用者和程式之間建立一個通訊通道,允許使用者通過終端對談與程式進行互動

PTY通常由兩個主要部分組成:主裝置(leader)和從裝置(follower)。leader端連線到使用者終端,follower端連線到一個或多個程式

當用戶開啟終端並啟動一個 shell 時,終端模擬器會建立一個 PTY,並將 leader 端連線到使用者介面,同時將 follower 端連線到 shell 或其他命令列程式。使用者輸入的命令通過 leader 端傳輸到 follower端,follower端執行這些命令並將輸出傳送回 leader 端,最終顯示在使用者介面上

在 Unix 中,一切皆檔案,這句話指的是 Unix 中的所有東西都有與檔案相同的讀/寫介面。leader 的 fd(檔案描述符) 指向記憶體中的一個緩衝區,而 follower 是一個在磁碟上具有實際路徑的字元裝置檔案。

上圖可以看到,我們開啟了兩個終端(/dev/pts/0、/dev/pts/1),啟動了兩個 shell 程序。如果我們在終端1(/dev/pts/1)中敲命令並重定向到終端0(/dev/pts/0),可以看到輸出結果是在終端0中顯示的

生成 shell

終端對談在啟動時可能會為shell建立一個子程序,這個子程序將作為 shell 的範例來執行使用者的命令

UNIX 和類 UNIX 系統中,終端對談會使用偽終端裝置(PTY)來與 shell 程序進行通訊,通過這種方式,終端對談可以讀取和寫入 shell 的輸入、輸出和錯誤輸出(fd 0到2)

shell 初始化

在Linux中,使用者開啟終端啟動 shell 程序時會進行 shell 初始化,這個過程涉及一些組態檔和指令碼的執行,用來設定使用者的環境和啟動 shell 的行為

步驟大致如下:

  1. 讀取組態檔:在使用者登入時,shell 會讀取一系列的組態檔來設定使用者的環境變數、別名、函數等。這些組態檔可以包括全域性組態檔(例如/etc/profile)和使用者特定的組態檔(例如~/.bash_profile~/.bashrc等)
  2. 執行設定命令:組態檔中可以包含各種設定和命令,例如設定環境變數、修改提示符、定義別名和函數等。這些命令會在 shell 啟動時執行,以確保在使用者登入後設定了所需的環境和行為
  3. 啟動shell:一旦執行了組態檔中的命令,shell 就會準備就緒,等待使用者的輸入。這時,shell 的提示符會出現,等待使用者輸入命令。