歡迎轉(zhuǎn)載,請保留作者信息
http://billstone.
對終端進(jìn)行讀寫
在編寫程序時(shí),我們往往需要從終端讀入數(shù)據(jù)。一種情況是需要連續(xù)地讀入用戶鍵入的選擇項(xiàng),這往往出現(xiàn)在數(shù)據(jù)庫程序中。程序員往往會(huì)使用getchar函數(shù)來讀取數(shù)據(jù),繼而判斷輸入的數(shù)據(jù)是否有效,從而做出反應(yīng)。其實(shí)如此做帶有很大的風(fēng)險(xiǎn),一個(gè)實(shí)例程序如下
#include <stdio.h>
char *menu[] = {
"a - add new record",
"d - delete record",
"q - quit",
NULL
};
int getchoice(char *choices[]){
int chosen = 0;
int selected;
char **option;
do {
option = choices;
while(*option){
printf("%s\n", *option);
option++;
}
selected = getchar();
option = choices;
while(*option){
if(option[0] == selected){
chosen = 1;
break;
}
option++;
}
if(!chosen){
printf("Incorrect choice, select again.\n");
}
} while(!chosen);
return selected;
}
int main()
{
int choice = 0;
do{
choice = getchoice(menu);
printf("You choose %c\n", choice);
} while('q' != choice);
exit(0);
}
|
實(shí)例程序中,用戶需要鍵入“A/回車/Q/回車”才能做出選擇。但這種處理有著很大的風(fēng)險(xiǎn),讀者可以自己測試一下。這也是初學(xué)者經(jīng)常碰到的問題。
默認(rèn)情況下,只有當(dāng)用戶按下回車鍵后,程序才能讀到終端的輸入。這種處理方式是規(guī)范模式或標(biāo)準(zhǔn)模式。在這種模式下,所有的輸入都給予行進(jìn)行處理,在一個(gè)輸入行完成前,終端接口負(fù)責(zé)管理所有的用戶鍵盤輸入,包括退格鍵,應(yīng)用程序讀不到用戶輸入的任何字符。
與標(biāo)準(zhǔn)模式相對的另一種模式為非標(biāo)準(zhǔn)模式,這種模式下,應(yīng)用程序?qū)τ脩糨斎胱址奶幚頁碛懈蟮目刂茩?quán)。
在上述程序中,Linux會(huì)暫存用戶讀入的內(nèi)容,直到用戶按下回車鍵,然后將用戶選擇的字符及緊隨其后的回車符一起傳送到程序。所以,每當(dāng)你選擇一個(gè)菜單時(shí),程序就調(diào)用getchar函數(shù)處理該字符,而當(dāng)程序在下一次循環(huán)再次調(diào)用getchar函數(shù)時(shí),它會(huì)立刻返回一個(gè)回車符。一個(gè)解決方案是程序在每次讀入數(shù)據(jù)前首先清空回車鍵之前的所有數(shù)據(jù),典型代碼如下:
do{
selected = getchar();
} while('\n' != selected);
|
終端驅(qū)動(dòng)程序和通用終端接口
有時(shí),程序需要更加精細(xì)的終端控制能力,而不是僅通過簡單的文件操作來完成對終端的一些控制。Linux提供了一組編程接口,這使得我們能夠控制終端驅(qū)動(dòng)程序的行為,從而允許我們對終端的輸入和輸出進(jìn)行更好的控制。
有一組函數(shù)調(diào)用(GTI)用作控制終端,這組函數(shù)調(diào)用與用于讀寫數(shù)據(jù)的函數(shù)是分離的,這就使得讀寫數(shù)據(jù)的接口非常簡潔,同時(shí)又保證用戶可以對終端的行為進(jìn)行更精細(xì)的控制。
termios結(jié)構(gòu)
通過設(shè)置termios結(jié)構(gòu)中的值和使用一組函數(shù)調(diào)用,我們就可以對終端接口進(jìn)行控制。
提示:使用termios結(jié)構(gòu)及相關(guān)的函數(shù)調(diào)用,需要包含termios.h頭文件;同時(shí)需要包含curses函數(shù)庫。
控制終端的操作模式有以下幾種:輸入模式、輸出模式、控制模式、本地模式和特殊的控制字符。具體操作由tcgetattr函數(shù)和tcsetattr函數(shù)來完成。其中,本地模式是最常用,也是最重要的一種操作模式。
注意:程序要將終端設(shè)置恢復(fù)到程序開始運(yùn)行之前的狀態(tài),這一點(diǎn)是非常重要的。首先保存這些值,然后在程序結(jié)束時(shí)恢復(fù)它們,這永遠(yuǎn)是程序的職責(zé)。
輸入模式控制輸入數(shù)據(jù)在被傳遞給程序之前的處理方式。通過設(shè)置termios結(jié)構(gòu)中的c_iflag成員的標(biāo)志對它們進(jìn)行控制。
輸出模式控制輸出字符的處理方式,即由程序發(fā)送出去的字符在傳遞到串行口或屏幕之前是如何處理的。通過設(shè)置termios結(jié)構(gòu)中c_oflag成員的標(biāo)志對輸出模式進(jìn)行控制。
控制模式控制終端的硬件特性。通過設(shè)置termios結(jié)構(gòu)中的c_cflag成員的標(biāo)志對控制模式進(jìn)行配置??刂颇J街饕糜诖芯€連接調(diào)制解調(diào)器的情況。
本地模式控制終端的各種特性。通過設(shè)置termios結(jié)構(gòu)中的c_lflag成員的標(biāo)志對本地模式進(jìn)行配置。其中最常用的兩個(gè)標(biāo)志是ECHO和ICANON。前者抑制鍵入字符的回顯,后者將終端在兩個(gè)截然不同的接收字符處理模式之間進(jìn)行切換。如果設(shè)置了ICANON標(biāo)志,就啟用標(biāo)準(zhǔn)輸入行處理模式,否則就啟動(dòng)非標(biāo)準(zhǔn)模式。
當(dāng)用戶鍵入類似Ctrl-C這樣的組合鍵時(shí),終端會(huì)采取一些特殊的處理方式。termios結(jié)構(gòu)中的c_cc數(shù)組成員將各種特殊的控制字符映射到對應(yīng)的支持函數(shù)。每個(gè)字符的位置是由一個(gè)宏定義的,但不限制這些字符必須是控制字符。
注意:在兩種不同的模式(標(biāo)準(zhǔn)模式和非標(biāo)準(zhǔn)模式)下,c_cc數(shù)組的下標(biāo)值有一部分是重疊的。出于這個(gè)原因,一定要注意不要將兩種模式各自的下標(biāo)值混淆。
可以通過stty命令查詢及修改終端模式。
通過termios結(jié)構(gòu)我們還可以控制終端的傳入和傳出的速度(波特率)。
終端的輸出
編寫能夠應(yīng)付連接到UNIX系統(tǒng)上的各種不同類型終端的程序看上去是一件非常讓人畏懼的事情。因?yàn)檫@樣的程序必須針對各種類型的終端編寫相應(yīng)的代碼。termifo軟件包的出現(xiàn)解決了這一問題。在絕大多數(shù)現(xiàn)代的UNIX系統(tǒng)上,這個(gè)軟件包和另一個(gè)軟件包curses集成在一起。
注意:在Linux系統(tǒng)上,在使用termifo軟件包時(shí)可能需要包含ncurses庫;該庫實(shí)現(xiàn)了curses軟件包的所有功能。
termifo的功能標(biāo)識由屬性描述,它們被保存在一組編譯好的terminfo文件中,而這些文件可以方便地在/usr/lib/terinfo或/usr/share/terinfo目錄下找到。例如,VT100終端的定義就放在文件/usr/share/terminfo/v/vt100中。你可以使用infocmp程序輸出terminfo編譯數(shù)據(jù)項(xiàng)的可讀版本。
虛擬控制臺
在Linux的典型安裝中將配置12個(gè)虛擬控制臺。虛擬控制臺通過字符設(shè)備文件/dev/ttyN使用,tty是Teletype的縮寫,而N代表一個(gè)數(shù)字,從1開始。
通過who和ps命令,可以查看目前登錄進(jìn)系統(tǒng)的用戶,以及目前在使用的虛擬控制臺及其上運(yùn)行的shell和程序。
Linux系統(tǒng)一般在前六個(gè)虛擬控制臺上運(yùn)行一個(gè)getty進(jìn)程,這樣用戶即可用同一個(gè)屏幕、鍵盤和鼠標(biāo)在六個(gè)不同的虛擬控制臺上登錄??梢酝ㄟ^組合鍵Ctrl+Alt+F<N>在這六個(gè)不同的虛擬控制臺之間進(jìn)行切換。
如果Linux系統(tǒng)使用的是圖形登錄界面或者使用startx切入圖形界面,X視窗系統(tǒng)將使用第一個(gè)未使用的控制臺,通常是/dev/tty7。
而偽終端由字符設(shè)備文件/dev/pty使用,其中pty是pseudo tty的縮寫。它與tty終端的區(qū)別在于偽終端沒有對于的硬件設(shè)備。
運(yùn)程登錄的終端由字符設(shè)備文件/dev/pts/N使用