终端
原文鏈接:http://www.orlion.ga/1227/
一、概念
????unix系統(tǒng)中用戶通過終端登錄系統(tǒng)后得到一個Shell進(jìn)程,這個終端成為Shell進(jìn)程的控制端。控制終端是保存在PCB中的信息,而fork會復(fù)制PCB中的信息,因此Shell進(jìn)程啟動的其他進(jìn)程的控制終端也是這個終端。默認(rèn)情況下(沒有重定向),每個進(jìn)程的標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯誤輸出都指向控制終端,進(jìn)程從標(biāo)準(zhǔn)輸入讀也就是讀用戶的鍵盤輸入,進(jìn)程往標(biāo)準(zhǔn)輸出或標(biāo)準(zhǔn)錯誤輸出寫也就是輸出到顯示器上。另外在控制終端輸入一些特殊的控制鍵可以給前臺進(jìn)程發(fā)信號,如:Ctrl-C表示SIGINT,Ctrl-\表示SIGQUIT。
????每個進(jìn)程可以通過特殊的設(shè)備文件/dev/tty訪問它的控制終端,事實上每個終端設(shè)備都對應(yīng)一個不同的設(shè)備文件,/dev/tty提供了一個通用的接口,一個進(jìn)程要訪問它的控制終端既可以通過/dev/tty也可以通過該終端設(shè)備所對應(yīng)的設(shè)備文件來訪問。ttyname函數(shù)可以由文件描述符查出對應(yīng)的文件名,該文件描述符必須指向一個終端設(shè)備而不能是任意文件。
二、終端登錄過程
????一臺PC通常只有一套鍵盤和顯示器,也就是只有一套終端設(shè)備,但是可以通過Ctrl-Alt-F1~Ctrl-Alt-F6切換到6個終端,相當(dāng)于有六套虛擬的終端設(shè)備,它們公用同一套物理終端設(shè)備,對應(yīng)的設(shè)備文件分別是/dev/tty1~/dev/tty6,所以稱為虛擬終端。設(shè)備文件/dev/tty0表示當(dāng)前虛擬終端,比如切換到Ctrl-Alt-F1的字符終端時/dev/tty0就表示/dev/tty1,切換到Ctrl-Alt-F2的字符終端時/dev/tty0就表示/dev/tty2,就像/dev/tty一樣也是一個通用的接口,但它不能表示圖形終端窗口所對應(yīng)的終端。
? ?內(nèi)核中處理終端設(shè)備的模塊包括硬件驅(qū)動程序和線路規(guī)程,如下圖所示:
????
????硬件驅(qū)動程序負(fù)責(zé)讀寫實際的硬件設(shè)備,比如從鍵盤讀入字符和把字符輸出到顯示器,線路規(guī)程像一個過濾器,對于某些特殊字符并不是讓它直接通過,而是做特殊處理,比如在鍵盤上按Ctrl-Z,對應(yīng)的字符并不會被用戶程序的read讀到,而是被線路規(guī)程截獲,解釋成SIGTSTP信號發(fā)給前臺進(jìn)程,通常會使該進(jìn)程停止。線路規(guī)程應(yīng)該過濾哪些字符和做哪些特殊處理是可以配置的。
????終端設(shè)備有輸入和輸出隊列緩沖區(qū),如下圖所示:
????
????以輸入隊列為例,從鍵盤輸入的字符經(jīng)線路規(guī)程過濾后進(jìn)入輸入隊列,用戶程序以先進(jìn)先出的順序從隊列中讀取字符,一般情況下,當(dāng)輸入隊列滿的時候再輸入字符會丟失,同時系統(tǒng)會響鈴警報。終端可以配置成回顯(Echo)模式,在這種模式下,輸入隊列中的每個字符既送給用戶程序也送給輸出隊列,因此我們在命令行鍵入字符時,該字符不僅可以被程序讀取,我們也可以同時再屏幕上看到該字符的回顯。
????終端登錄過程:
????1.系統(tǒng)啟動時,init進(jìn)程根據(jù)配置文件/etc/inittab確定要打開哪些終端。例如配置文件中有這樣一行:
1:2345:respawn:/sbin/getty?9600?tty1????和/etc/passwd類似,每個字段用:號隔開。開頭的1是這一行配置的id,通常要和tty的后綴一致,配置tty2的那一行id就應(yīng)該是2。第二個字段2345表示運行級別2~5都執(zhí)行這個配置。最后一個字段/sbin/getty 9600 tty1是init進(jìn)程要fork/exec命令,打開終端/dev/tty1,波特率是9600(波特率只對串口和Modem終端有意義),然后提示用戶輸入賬號。中間的respwan字段表示init進(jìn)程會監(jiān)視getty進(jìn)程的運行狀態(tài),一旦該進(jìn)程終止,init會再次fork/exec這個命令,所以我們從終端退出登錄后會再次提示輸入賬號。有些新的Linux發(fā)行版已經(jīng)不用/etc/inittab這個配置文件了,例如Ubuntu用/etc/event.d目錄下的配置文件來配置init。
????2.getty根據(jù)命令行參數(shù)打開終端設(shè)備作為它的控制終端,把文件描述符0、1、2都指向控制終端,然后提示用戶輸入賬號。用戶輸入賬號之后,getty的任務(wù)就完成了,它再執(zhí)行l(wèi)ogin程序:
execle("/bin/login",?"login",?"-p",?username,?NULL,?envp);????3.login程序提示用戶輸入密碼(輸入密碼期間關(guān)閉終端的回顯),然后驗證賬號密碼的正確性,如果密碼不正確,login進(jìn)程終止,init會重新fork/exec一個getty進(jìn)程。如果密碼正確,login程序設(shè)置一些環(huán)境變量,設(shè)置當(dāng)前工作目錄為該用戶的主目錄,然后執(zhí)行Shell:
execl("/bin/bash",?"-bash",?NULL);????注意argv[0]參數(shù)的程序名前加了一個-,這樣bash就知道自己是作為登錄Shell啟動的,執(zhí)行登錄Shell的啟動腳本。從getty開始exec到login,再exec到bash,其實都是同一個進(jìn)程,因此控制終端沒變,文件描述符0、1、2也仍然指向控制終端。由于fork會復(fù)制PCB信息,所以由Shell啟動的其他進(jìn)程也都是如此。
?
三、網(wǎng)絡(luò)登錄過程
????虛擬終端或串口終端的數(shù)目是有限的,虛擬終端一般就是/dev/tty1~/dev/tty6六個,串口終端的數(shù)目也不超過串口的數(shù)目。然而網(wǎng)絡(luò)終端或圖形終端的數(shù)目卻是不受限制的,這是通過偽終端實現(xiàn)的。一套偽終端由一個主設(shè)備和一個從設(shè)備組成。主設(shè)備在概念上相當(dāng)于鍵盤和顯示器,只不過它不是真正的硬件而是一個內(nèi)核模塊,操作它的也不是用戶而是另外一個進(jìn)程。從設(shè)備和上面介紹的/dev/tty1這樣的終端設(shè)備模塊類似,只不過他的底層驅(qū)動程序不是訪問硬件而是訪問主設(shè)備。網(wǎng)絡(luò)終端或圖形終端窗口的Shell進(jìn)程以及它的啟動的其它進(jìn)程都會認(rèn)為自己的控制終端是偽終端從設(shè)備,例如/dev/pts/0、/dev/pts/1等。下面以telnet為例說明網(wǎng)絡(luò)登錄和使用偽終端的過程:
????
????1.用戶通過telnet客戶端連接服務(wù)器。如果服務(wù)器配置為獨立模式,則在服務(wù)器監(jiān)聽連接請求時一個telnetd進(jìn)程,它fork出一個telnetd子進(jìn)程來服務(wù)客戶端,父進(jìn)程仍監(jiān)聽其他連接請求。
????另外一種可能是服務(wù)器端由系統(tǒng)服務(wù)程序inetd或xinetd監(jiān)聽連接請求,inetd稱為Internet Super-Server,它監(jiān)聽系統(tǒng)中的多個網(wǎng)絡(luò)服務(wù)端口,如果連接請求的端口號和telnet服務(wù)端口號一致,則fork/exec一個telnetd子進(jìn)程來服務(wù)客戶端。xinetd是inetd的升級版本,配置更為靈活。
????2.telnetd子進(jìn)程打開一個偽終端設(shè)備,然后再經(jīng)過fork一分為二:父進(jìn)程操作偽終端主設(shè)備,子進(jìn)程將偽終端從設(shè)備作為它的控制終端,并且將文件描述符0、1、2指向控制終端,二者通過偽終端通信,父進(jìn)程還負(fù)責(zé)和telnet客戶端通信,而子進(jìn)程負(fù)責(zé)用戶的登錄過程,提示輸入賬號,然后調(diào)用exec變成login進(jìn)程,提示輸入密碼,然后調(diào)用exec變成Shell進(jìn)程。這個Shell進(jìn)程認(rèn)為自己的控制終端是偽終端從設(shè)備,偽終端主設(shè)備可以看作鍵盤顯示器等硬件,而操作這個偽終端的"用戶"就是父進(jìn)程telnetd。
????3.當(dāng)用戶輸入命令時,telnet客戶端將用戶輸入的字符通過網(wǎng)絡(luò)發(fā)給telnetd服務(wù)器,由telnetd服務(wù)器代表用戶將這些字符輸入偽終端。Shell進(jìn)程并不知道自己連接的是偽終端而不是真正的鍵盤顯示器,也不知道操作終端的"用戶"其實是telnetd服務(wù)器而不是真正的用戶。Shell仍然解釋執(zhí)行命令,將標(biāo)準(zhǔn)輸入和標(biāo)準(zhǔn)錯誤輸出寫到終端設(shè)備,這些數(shù)據(jù)最終由telnetd服務(wù)器發(fā)回給telnet客戶端,然后顯示給用戶看。
?
????如果telnet客戶端和服務(wù)器之間的網(wǎng)絡(luò)延遲較大,我們會觀察到按下一個鍵之后要過幾秒鐘才能回顯到屏幕上。這說明我們每按一個鍵telnet客戶端都會立刻把該字符發(fā)送給服務(wù)器,然后字符經(jīng)過偽終端主設(shè)備和從設(shè)備之后被Shell進(jìn)程讀取,同時回顯到偽終端從設(shè)備,回顯的字符再經(jīng)過偽終端主設(shè)備、telnetd服務(wù)器和網(wǎng)絡(luò)發(fā)回給telnet客戶端,顯示給用戶看。
????BSD系列的UNIX在/dev目錄下創(chuàng)建很多ptyXX和ttyXX設(shè)備文件,XX由字母和數(shù)組組成,ptyXX是主設(shè)備,相對應(yīng)的ttyXX是從設(shè)備,偽終端的數(shù)目取決于內(nèi)核配置。而在SYS V系列的UNIX上,偽終端主設(shè)備是/dev/ptmx,"mx"表示Multiplex,意思是多個主設(shè)備復(fù)用同一個設(shè)備文件,每打開一次/dev/ptmx,內(nèi)核就分配一個主設(shè)備,同時在/dev/pts目錄下創(chuàng)建一個從設(shè)備文件,當(dāng)終端關(guān)閉時就從/dev/pts目錄下刪除相應(yīng)的從設(shè)備文件。Linux同時支持上述兩種偽終端,目前的標(biāo)準(zhǔn)傾向于SYS V的偽終端。
與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖總結(jié)
- 上一篇: 子弹射中敌人播放爆炸并销毁的动画效果
- 下一篇: 响应者链