一、綜述
終端IO有兩種不同的工作方式:
- 規範方式輸入處理。在這種方式中,終端輸入以行爲單位進行處理。對於每個讀要求,終端驅動程序最多返回一行。
- 非規範方式輸入處理。輸入字符不以行爲單位進行裝配
如果不做特殊處理,則默認方式是規範方式。vi編輯程序使用非規範方式,其原因是其命令是由不以換行符終止的一個或幾個字符組成。
POSIX.1定義了11個特殊輸入字符。其中9個可以改變
終端設備一般位於內核中的終端驅動程序所控制的。每個終端設備有一個輸入隊列,一個輸出隊列,見下圖:
對此圖要說明以下幾點:
- 如果需要回送,則在輸入隊列和輸出隊列中間有一個隱含的連接。
- 輸入隊列的長度MAX_INPUT是有限值,當一個特定設備的輸入隊列已填滿,各系統實現有所不同,大多數UNIX系統會送響鈴字符。
- 圖中沒有顯示另一個輸入限制MAX_CANON,它是一個規範輸入行中的最大字節數。
- 雖然輸出隊列通常也是有限長度,但是程序不能存取定義其長度的常數。這是因爲當輸出隊列要填滿時,內核使寫進程睡眠直至寫隊列中有可用的空間,所以程序無需關心該隊列的長度。
大多數UNIX系統在一個稱爲終端行規程(terminal line discipline)的模塊中進行規範處理。它是位於內核類屬讀、寫函數和實際設備驅動程序之間的模塊,見下圖
所有我們可以檢測和更改的終端設備特性都包含在termios結構中。該結構在頭文件<termios.h>中定義。
struct termios {
tcflag_t c_iflag; /* input flag */
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 */
};
粗略而言,輸入標誌由終端設備驅動程序用來控制輸入特性(剝除輸入字節第8位,允許輸入奇偶校驗等等),輸出標誌則控制輸出特性(執行輸出處理,將新行映照爲CR/LF等),控制標誌影響到RS-232串行線(忽略調制解調器的狀態線,每個字符的一個或兩個停止位等等),本地標誌影響驅動程序和用戶之間的界面(回送的開或關,可試的擦除符,允許終端產生的信號,對後臺作業輸出的控制停止信號等)。
類型tcflag_t的長度是以保持每個標誌值。它經常被定義爲unsigned long。c_cc數組包含了所有可以更改的特殊字符。NCCS是該數組的長度,POSIX.1定義爲11,咯實現有所不同。cc_t類型的長度足以保持每個專用字符,典型的是unsigned char。
下表列出了所有可以更改以影響終端設備特性的終端標誌。
下表列出了POSIX.1定義對終端設備進行操作的各個函數。
對終端設備,POSIX.1沒有使用ioctl,而使用了上表中列出的12個函數。這樣做的理由是:對於終端設備的ioctl函數,最後一個參數的數據類型隨執行動作的不同而不同。
上表中12個函數之間的關係見下圖:
二、特殊輸入字符
POSIX.1定義了11個在輸入時做特殊處理的字符。SVR4 另外加了6個特殊字符,4.3+BSD則加了7個。下表列出了這些字符
11個特殊字符中,9個可以更改爲任何值。不能更改的是換行符\n和回車符\r。爲了進行修改,只要更改termios結構中c_cc數組的相應項。該數組中的元素都用名字作爲下標進行引用,每個名字都以字母V開頭。
三、獲取和設置終端屬性
使用函數tcgetattr和tcsetattr可獲得/設置termios。這樣就可以檢測和修改各種終端選擇標誌和特殊字符,以使終端按我們所希望的方式進行操作。
#include <termios.h>
int tcgetattr(int filedes, struct termios *termpptr);
int tcsetattr(int filedes, int opt, const struct termios *termpptr);\
返回值: 成功爲1,出錯爲-1
因爲這兩個函數只對終端設備進行操作,所以若filedes並不引用一個終端設備則出錯返回,error設置爲ENOTTY
tcsetattr的參數opt使我們可以指定在什麼時候新的終端屬性才起作用。opt可以指定爲以下常數中的一個:
- TCSANOW 更改立即發生
- TCSADRAIN 發送了所有輸出後更改才發生。若更改輸出參數則應使用此選擇項
- TCSAFLUSH 發送了所有輸出後更改才發生。更進一步,在更改發生時未讀的所有輸入數據都被刪除(刷清)。
tcsetattr函數的返回值易於產生混淆。如果執行了任意一種所要求的動作,即使未能執行所有要求的動作,它也返回0(表示成功)。如果函數返回0,則我們有必要調用tcgetattr獲取實際終端屬性與希望的終端屬性做對比,判斷是否真正的修改成功。