當(dāng)你打開終端并輸入命令時會發(fā)生什么?
哈嘍大家好,我是咸魚
參加過校招面試的小伙伴們肯定對下面這道面試題很熟悉:“當(dāng)你在瀏覽器輸入一段網(wǎng)址后會發(fā)生什么?”。這道面試題可以說是很經(jīng)典了,因為其涉及大量網(wǎng)絡(luò)協(xié)議,可以非常直觀的看出小伙伴們對計算機網(wǎng)絡(luò)體系的整體把握程度
但如果問題換成:“當(dāng)你打開終端并輸入 ls 時會發(fā)生什么?”,有多少小伙伴能夠回答出來呢?
終端的前世今生
大多數(shù)現(xiàn)代終端應(yīng)用程序的工作方式都來自于其歷史前輩——電傳打字機(teletypes,簡稱 tty)
在大型計算機的時代,當(dāng)時數(shù)據(jù)存儲在磁帶上,計算機的內(nèi)存以 kB 為單位,電傳打字機就是為了它們而被設(shè)計出來
圖片
左邊的是 IBM 2741電傳打字機,右邊是 IBM System/360 Mo. 40大型計算機
電傳打字機是允許用戶與計算機交互的基本文本客戶端。teletypes 其實是 teletypewriter的縮寫,因為它是從打字機(typewriters)演變過來的
如上圖所示,電傳打字機和大型計算機通過連接兩端的物理線來進(jìn)行通信。溝通過程如下:
- 當(dāng)用戶從電傳打字機輸入時,ASCII 文本將一個字符一個字符地通過網(wǎng)絡(luò)傳輸
- 計算機的內(nèi)核接收字符并對其進(jìn)行解碼
- 接著字符被送到一個名為 TTY driver 的驅(qū)動程序,這里負(fù)責(zé)將輸入發(fā)送到用戶程序并收集輸出
- 最后,內(nèi)核將輸出發(fā)送回電傳打字機 ,以便顯示給用戶
需要提到的一點是 line discipline(行規(guī)則),它會將字符緩沖到內(nèi)核內(nèi)存中,直到按下 Enter 鍵,程序才會接收到輸入
line discipline 允許這塊緩沖區(qū)是可編輯的,并提供了一些與程序無關(guān)的快捷鍵(例如 ctrl-w)
這在當(dāng)時是一項重要的性能優(yōu)化,因為讓程序員一個字符一個字符的處理是非常低效的
隨著計算技術(shù)的進(jìn)步,這些獨立組件中的許多都實現(xiàn)了現(xiàn)代化。比如說電傳打字機被終端所取代,終端是完全電子的機器,包括電子顯示器
圖片
DEC 于 1978 年發(fā)布的 VT100 終端機(VT = video terminal),它實現(xiàn)并推廣了至今仍在使用的 ANSI 轉(zhuǎn)義碼
隨著電子終端的誕生,出現(xiàn)了越來越多的功能(例如顏色、鈴聲)。但本質(zhì)上跟電傳打字機完全相同——發(fā)送輸入字符流并顯示輸出
現(xiàn)如今人人都有一臺自己的電腦,這些電腦的操作系統(tǒng)可以監(jiān)督許多應(yīng)用程序,終端不再是專門的硬件,而是變成了這些應(yīng)用程序中的一個
圖片
與典型的 GUI 應(yīng)用程序一樣,終端是操作系統(tǒng)監(jiān)督下的一個進(jìn)程,它監(jiān)聽來自用戶的事件和輸入,并告訴操作系統(tǒng)在窗口中顯示什么(終端不直接與外設(shè)交互,而是通過驅(qū)動程序和窗口管理器)
有時候我們還會聽到 ”終端模擬器“ 這個詞,而不是簡單的稱之為 ”終端“。這是因為 ”終端“ 指的是專門的硬件(終端機),而現(xiàn)在大多數(shù)的終端只是對該設(shè)備的模擬,是一個應(yīng)用程序
但是我們這里不做區(qū)分,下文提到的“終端”等同于“終端模擬器”
那么當(dāng)我們打開終端時會發(fā)生什么呢?
打開終端
上面我們提到過,終端是一個應(yīng)用程序,能夠讓你 ”使用你的電腦“(即在上面運行程序)。我們的電腦上可能已經(jīng)存在了 ls、rm、mv 等程序
但是我們不滿足于使用這些簡單的命令,我們還希望使用腳本來實現(xiàn)自動化, 這些腳本將許多命令的序列組合在一起,使用分支條件邏輯,運行重復(fù)循環(huán)或并行化命令等
為了讓計算機能夠讀懂我們的腳本并執(zhí)行起來,我們需要一個完整的可交互的解釋型的編程環(huán)境——shell
將其他程序作為進(jìn)程運行,讓操作系統(tǒng)內(nèi)核讀懂你寫的腳本,這些工作都由 shell 完成。目前常見的 shell 有 Bash、Zsh 等
終端和 shell 是兩個獨立的程序:
- shell 負(fù)責(zé)解釋你輸入的命令
- 終端負(fù)責(zé) UI 相關(guān)的東西,比如字體、顏色等
簡單來講,當(dāng)我們打開終端時,終端會根據(jù)用戶生成一個 shell 進(jìn)程,以及用戶與 shell 之間,用戶與 shell 啟動的進(jìn)程之間通信的方法
這個 shell 進(jìn)程負(fù)責(zé)解釋和執(zhí)行用戶輸入的命令,并與用戶進(jìn)行交互。用戶在終端輸入的命令將通過這個通信通道傳遞給 shell 進(jìn)程進(jìn)行解釋執(zhí)行,并將執(zhí)行結(jié)果反饋給用戶顯示在終端上
創(chuàng)建 PTY
偽終端設(shè)備(PTY)是在計算機操作系統(tǒng)中創(chuàng)建的一個虛擬設(shè)備,用于模擬物理終端的功能
在 UNIX、Linux 和類 UNIX 系統(tǒng)中,PTY 用于在用戶和程序之間建立一個通信通道,允許用戶通過終端會話與程序進(jìn)行交互
PTY通常由兩個主要部分組成:主設(shè)備(leader)和從設(shè)備(follower)。leader 端連接到用戶終端,follower 端連接到一個或多個程序
圖片
當(dāng)用戶打開終端并啟動一個 shell 時,終端模擬器會創(chuàng)建一個 PTY,并將 leader 端連接到用戶界面,同時將 follower 端連接到 shell 或其他命令行程序
用戶輸入的命令通過 leader 端傳輸?shù)?nbsp;follower 端,follower 端執(zhí)行這些命令并將輸出發(fā)送回 leader 端,最終顯示在用戶界面上
在 Unix 中,一切皆文件,這句話指的是 Unix 中的所有東西都有與文件相同的讀/寫接口。
leader 的 fd(文件描述符) 指向內(nèi)存中的一個緩沖區(qū),而 follower 是一個在磁盤上具有實際路徑的字符設(shè)備文件
圖片
上圖可以看到,我們打開了兩個終端(/dev/pts/0、/dev/pts/1),啟動了兩個 shell 進(jìn)程。如果我們在終端 1(/dev/pts/1)中敲命令并重定向到終端0(/dev/pts/0),可以看到輸出結(jié)果是在終端 0 中顯示的
生成 shell
終端會話在啟動時可能會為 shell 創(chuàng)建一個子進(jìn)程,這個子進(jìn)程將作為 shell 的實例來執(zhí)行用戶的命令
UNIX 和類 UNIX 系統(tǒng)中,終端會話會使用偽終端設(shè)備(PTY)來與 shell 進(jìn)程進(jìn)行通信,通過這種方式,終端會話可以讀取和寫入 shell 的輸入、輸出和錯誤輸出(fd 0到2)
這樣使得用戶在終端輸入的命令可以被 Shell 進(jìn)程解釋執(zhí)行,并且 Shell 進(jìn)程的輸出可以在終端顯示
圖片
shell 初始化
在 Linux 中,用戶打開終端啟動 shell 進(jìn)程時會進(jìn)行 shell 初始化,這個過程涉及一些配置文件和腳本的執(zhí)行,用來設(shè)置用戶的環(huán)境和啟動 shell 的行為
步驟大致如下:
- 讀取配置文件:在用戶登錄時,shell 會讀取一系列的配置文件來設(shè)置用戶的環(huán)境變量、別名、函數(shù)等。這些配置文件可以包括全局配置文件(例如/etc/profile)和用戶特定的配置文件(例如~/.bash_profile、~/.bashrc等)
- 執(zhí)行配置命令:配置文件中可以包含各種設(shè)置和命令,例如設(shè)置環(huán)境變量、修改提示符、定義別名和函數(shù)等。這些命令會在 shell 啟動時執(zhí)行,以確保在用戶登錄后設(shè)置了所需的環(huán)境和行為
- 啟動shell:一旦執(zhí)行了配置文件中的命令,shell 就會準(zhǔn)備就緒,等待用戶的輸入。這時,shell 的提示符會出現(xiàn),等待用戶輸入命令