UNIX 终端IO
1.什么是Linux的終端I/O,它分為幾種類型。
?????? 終端是一種字符型設備,有多種類型,通常使用tty來簡稱各種類型的終端設備。終端特殊設備文件一般有以下幾種: 串行端口終端(/dev/ttySn) ,偽終端(/dev/pty/),控制終端(/dev/tty) ,控制臺終端(/dev/ttyn, /dev/console).
1. 串行端口終端(Serial Port Terminal)是使用計算機串行端口連接的終端設備。計算機把每個串行端口都看作是一個字符設備。串行端口所對應的設備名稱是/dev/tts/0(或/dev/ttyS0)、/dev/tts/1(或/dev/ttyS1)等,設備號分別是(4,0)、(4,1)等,分別對應于DOS系統下的COM1、COM2等。
2. 偽終端(Pseudo Terminal)是成對的邏輯終端設備,例如/dev/ptyp3和/dev/ttyp3(在設備文件系統中分別是/dev/pty/m3和/dev/pty/s3),它們與實際物理設備并不直接相關。對于ttyp3(s3),任何設計成使用一個串行端口設備的程序都可以使用該邏輯設備。但對于使用ptyp3的程序,則需要專門設計來使用ptyp3(m3)邏輯設備。 如果使用telnet遠程登錄,則telnet會開始連接到設備ptyp2(m2)上(一個偽終端端口上)。此時一個getty程序就應該運行在對應的ttyp2(s2)端口上。當telnet從遠端獲取了一個字符時,該字符就會通過m2、s2傳遞給getty程序,而getty程序就會通過s2、m2和telnet程序往網絡上返回”login:”字符串信息。登錄程序與telnet程序就通過“偽終端”進行通信,通過使用適當的軟件,就可以把兩個甚至多個偽終端設備連接到同一個物理串行端口上。因為只存在16個ttyp(ttyp0—ttypf)的設備文件,就使用了象q、r、s等字符來代替p。例如,ttys8和ptys8就是一個偽終端設備對。目錄/dev/pts是一個類型為devpts的文件系統,并且可以在被加載文件系統列表中看到。雖然/dev/pts象是設備文件系統中的一項,但它其實是一種不同的文件系統。
3. 如果當前進程有控制終端(Controlling Terminal)的話,那么/dev/tty就是當前進程的控制終端的設備特殊文件。可以使用命令”ps –ax”來查看進程與哪個控制終端相連使用命令”tty”可以查看它具體對應哪個實際終端設備。/dev/tty有些類似于到實際所使用終端設備的一個聯接。
4.? Linux系統中,計算機顯示器通常被稱為控制臺終端(Console),它仿真了類型為Linux的一種終端(TERM=Linux),并且有一些設備特殊文件與之相關聯:tty0、tty1、tty2等。當你在控制臺上登錄時,使用的是tty1。使用Alt+[F1—F6]組合鍵時,我們就可以切換到tty2、tty3等上面去。tty1 –tty6等稱為虛擬終端,而tty0則是當前所使用虛擬終端的一個別名,系統所產生的信息會發送到該終端上。只有系統或超級用戶root可以向/dev/tty0進行寫操作 .
終端IO有兩種模式:
??? 1. 規范模式。在這種模式下,輸入以行為單位進行處理。每次讀請求終端驅動都返回一行。
??? 2. 非規范模式。輸入字符不編成一行。
默認情況下系統采用規范模式。
POSIX。1定義了 11種特殊輸入字符,它們中的9種可以改變。
?
<termios.h>
Struct termios{
?????? Tcflag_t c_iflag;???? //input flags
?????? Tcflag_t c_oflag;??? //output flags
?????? Tcflag_t c_cflag;??? //control flags
?????? Tcflag_t c_lflag;???? //local flags
?????? Cc_t?????? ?? c_cc[NCCS]; //control characters??
};
?
輸入標志:控制從終端設置驅動程序輸入的字符。
輸出標志:控制輸出字符
控制標志:控制標志影響RS-232串行線
本地標志:本地標志影響驅動與用戶之間的接口。如開關回顯,顯示擦除字符,使終端產生信號,為后臺輸出的工作控制停止信號
?
Tcflag_t對于標志位值已經足夠大了,可以定義它為 unsigned int 或 unsigned long類型。
C_cc數組包含所有我們能改變的特別字符。Cc_t 通常定義為unsigned char
?
控制字符:
?????? CLOCAL: 忽略modem狀態線
?????? CREAD: 使 接收器中 開啟
?????? CRTSCTS: 使硬件流控制開啟
?????? CSIZE:??? 字符大小的掩碼
?????? CSTOPB: 發送兩個stop位,其它情況發送一個
?????? CUPCL:? 最后關閉時停止
?????? PARENB: 部分開啟
?????? PARODD: 奇數,其它為偶數
??????
?
輸入標志:
?????? BRKINT: 當BREAK時產生SIGINT信號
?????? ICRNL:?? 在輸入時將CR 轉成 NL
?????? IGNBRK : 忽略BREAK
?????? IGNCR:?? 忽略CR
?????? IGNPAR: 忽略字符
?????? IMAXBEL 當隊列滿時振玲
?????? INLCR? 將NL換成CR
?????? INPCK???
?????? IUCLC??? 大寫字符轉成小寫字符
?????? IXANY
?????? IXOFF 開始或關閉輸入流控制
?????? IXON
?????? PARMRK
?
本地標志:
?????? ECHO 開啟 echo
?????? ECHOCTL 回顯控制字符
?????? ECHOE 顯示擦除字符
?????? ECHOK 顯示KILL
?????? ECHOKE 為KILL顯示擦除字符
?????? ECHONL 顯示NL
?????? ECHOPRT 為硬拷貝顯示擦除字符
?????? EXTPROC 擴展字符
?????? FLUSHO
?????? ICANON 規范輸入
?????? IEXTEN
?????? ISIG 使終端產生信號開啟
?????? NOFLSH
?
……
?
終端IO總結:
?????? Tcgetattr 得到屬性(termios結構)
?????? Tcsetattr
?????? Cfgetispeed get input speed
?????? Cfgetospeed 得到輸出速度
?????? Cfsetispeed
?????? Cfsetospeed
?????? Tcdrain 等待所有輸出傳出
?????? Tcflow??? 掛起傳輸或接收
?????? Tcflush??
?????? Tcsendbreak 發送BREAK字符
?????? Tcgetpgrp 得到前臺進程組ID
?????? Tcsetpgrp 設置前臺組ID
?????? Tcgetsid? 得到控制session頭,進程組ID
每個終端設備有一個輸入隊列和一個輸出隊列
得到或調置終端屬性
有兩個函數很重要:tcgetattr 和 tcsetattr,它們都包含在<termios.h>頭文件中。原型如下 inttcgetattr(int filedes, struct termios *termptr); 和 int tcsetattr(intfiledes, int opt, const struct termios *termptr);
opt 的值可以有以下幾個:
TCSANOW (terminal control set attribute now),改變立即發生
TCSADRIN ,在所有輸出已經傳出后,發生改變。
TCSAFLUSH , 所有輸出已經傳出后,且當改變發生時,所有沒有輸入被讀的數據丟棄。
?
終端標識
??? 歷史上大多數版本的UNIX控制終端使用 /dev/tty. POSIX.1提供了我們能調用決定控
制終端名子的運行時函數。
??? #include <stdio.h>
??? char *ctermid(char *ptr);
??? 如果ptr非空,它指向 L_ctermid 數組的字節,且控制終端的名子存放在該數組中。如果ptr是空指針,函數為數組分配空間,進程控制終端的名子存放在該數組中。
????
??? isatty函數,當文件描述符指向的是終端設備,則返回真值。
??? ttyname函數,它返回終端設備的路徑名。
規范模式
??? 規范終端IO很簡單,我們發出一個讀請求后,終端驅動程序讀取一行信息后返回,有幾個條件引起讀返回:
??? 1. 當讀到請求的字節數時讀請求返回。
??? 2. 當讀請求遇到定界符時,讀請求返回。如NL,EOL,EOL2, EOF
??? 3. 如果捕獲到一個信號且函數不自動重啟,則讀請求探返回。
非規范模式
???非規范模式是指關閉termios結構中的c_lflag域的ICANON標志位。在非規范模式下,輸入數據不編進一行,下面的一些特殊字符也不進行處理:ERASSE, KILL, EOF,NL, EOL, EOL2, CR, REPRINT ,STATUS 和 WERASE。
??? 規范模式是容易的,系統每次返回一行。但在非規范模式下,系統知道什么時候返回數據給我們呢?
??? 解決辦法是系統或是在指定數量已經讀夠或是給定的時間超時時返回。該技術使用了兩個變量:MIN和TIME。
??? MIN : 指明了在讀返回之前應讀的最小字節數。
??? TIME : 指明了等待數據的時間
??? 它們一共有四種組合,每種情況描述如下:
??? 1. MIN >0, TIME>0
??????? TIMER指明從第一個接收到的字節開始計數。如果在超時前,接收到MIN個字節,那么返回字節數。如果發生了時間超時,至少有一個字節返回,因為計時器是從接收到第一個字節開始計數的。
???????
??? 2. MIN >0, TIME == 0
??????? 在這種情況下,直到讀到的字節數達到MIN個時才返回。
??? 3. MIN==0, TIME >0
??????? TIME的值從讀用read開始計算,如果收到一個字節或時間超時時返回。
??? 4. MIN==0, TIME==0
??????? 如果一些數據是有效的,則 read返回這些數據。如果沒有數據有效,則立即返回。?????? ?
終端窗口大小
??? 內核為每個終端和偽終端維護著一個winsize結構:
??? struct winsize{
??????? unsigned short ws_row;
??????? unsigned short ws_col;
??????? unsigned short ws_xpixel;
??????? unsigned short ws_ypixel;
????};
??? 規則如下:
??? ??? 1. 我們可以使用 ioctl 的 TIOCGWINSZ 得到該結構的當前值。
??????? 2. 我們可以使用 ioctl 的 TIOCSWINSZ 將新值保存到內核維護的結構中。如果這個新值與當前內核中存放的值不一樣,則SIGWINCH信號發送給當前進程組。
??????? 3. 當值發生變化時,除了保存結構的當前值和產生一個信號之外,內核不做其它事情。
????????
terncap, terminfo, curses
??? termcap 代表 terminal capability, 它表示 /etc/termcap文件和讀該文件的一列列程。termcap文件中包含各種終端的描述。如終端支持那種特性和使終端執行某些操作。
總結
- 上一篇: tcp连接探测Keepalive和心跳包
- 下一篇: C运行时库和标准C++库