文章目錄
termios 結構是在POSIX規範中定義的標準介面,它類似於系統V中的termio介面,通過設定termios類型的資料結構中的值和使用一小
組函數調用,你就可以對終端介面進行控制。
可以被調整來影響終端的值按照不同的模式被分為如下幾組:
1.輸入模式
2.輸出模式
3.控制模式
4.本地模式
5.特殊控制模式
最小的termios結構的典型定義如下:
struct termios
{
tcflag_t c_iflag;
tcflag_t c_oflag;
tcflag_t c_cflag;
tcflag_t c_lflag;
cc_t c_cc[NCCS];
};
結構成員的名稱與上面列出的5種參數類型相對應。
你可以調用函數tcgetattr來初始化一個終端對應的termios結構,該函數的原型如下:
#include<termios.h>int tcgetattr(int fd, struct termios *termios_p);
這個函數調用把當前終端介面變數的值寫入termios_p參數指向的結構。如果這些值其後被修改了,你可以通過調用函數tcsetattr來重新設定
終端介面。
#include<termios.h>int tcsetattr(int fd , int actions , const struct termios *termios_h);
參數actions控制修改方式,共有三種修改方式,如下所示。
1.TCSANOW:立刻對值進行修改
2.TCSADRAIN:等當前的輸出完成後再對值進行修改。
3.TCSAFLUSH:等當前的輸出完成之後,再對值進行修改,但丟棄還未從read調用返回的當前的可用的任何輸入。
接下來我們將分別對五種模式進行介紹。
一 輸入模式
輸入模式控制輸入資料在傳遞給程式之前的處理方式。你通過設定termios結構中的c_iflag成員的標誌對它們進行控制。所有的標誌都被定義為
宏,並可通過按位或的方式結合起來。
可用於c_iflag成員的宏如下所示:
BRKINT:當在輸入行中檢測到一個終止狀態時,產生一個中斷。
TGNBRK:忽略輸入行中的終止狀態。
TCRNL:將接受到的斷行符號符轉換為新行符。
TGNCR:忽略接受到的新行符。
INLCR:將接受到的新行符轉換為斷行符號符。
IGNPAR:忽略奇偶校檢錯誤的字元。
INPCK:對接收到的字元執行奇偶校檢。
PARMRK:對奇偶校檢錯誤作出標記。
ISTRIP:將所有接收的字元裁減為7位元。
IXOFF:對輸入啟用軟體流控。
IXON:對輸出啟用軟體流控。
如果BRKINT和TGNBRK標誌都未被設定,則輸入行中的終止狀態就被讀取為NULL(0X00)字元。
三.輸出模式
輸出模式控制輸出字元的處理方式,即由程式發出的字元在傳遞到串列口或螢幕之前如何處理.通過設定c_oflag成員的標識對輸出模式進行控制.
OPSOT:開啟輸出處理功能
ONLCR:將輸出中的分行符號轉換為斷行符號符
OCRNL:將斷行符號符轉換為分行符號
ONOCR:第0行不輸出斷行符號符
ONLRET:不輸出斷行符號符
NLDLY:分行符號延時選擇
CRDLY:斷行符號符延時
TABDLY:定位字元延時
...
輸出模式用得也不多
四.控制模式
控制模式控制終端的硬體特性,通過c_cflag成員標識配置.
CLOCAL:忽略所有數據機的狀態行
CREAD:啟用字元接收器
CS5/6/7/8:發送或接收字元時使用5/6/7/8位元
CSTOPB:每個字元使用兩停止位
HUPCL:關閉時掛斷數據機
PARENB:啟用同位碼的產生和檢測功能
PARODD:只使用奇檢驗而不用偶校正
一般也不用這種方式,通常直接修改終端設定檔來修改硬體特性要容易一些
五.本地模式
通過c_lflag成員控制終端的某些特性
ECHO:啟用輸入字元的本機回應功能
ECHONL:回顯分行符號
ICANON:啟用標準輸入處理
ISIG:啟用訊號
...
最常用的是ECHO和ICANON標誌,前者抑制鍵入字元的回顯(抑制??),後者如說明
六.特殊的控制字元
標準模式和非標準模式下,c_cc數組的下標有不同的值:
標準模式:
VEOF:EOF字元
VEOL:EOL字元
VERASE:ERASE字元
VINTR:INTR字元
VKILL:KILL字元
VQUIT:QUIT字元
VSTART:START字元
VSTOP:STOP字元
非標準模式:
VINTR:INTR字元
VMIN:MIN值
VQUIT:QUIT字元
VSUSP:SUSP字元
VTIME:TIME值
VSTART:START字元
VSTOP:STOP字元
1.字元
INTR:該字元使終端驅動程式向與終端相連的進程以送SIGINT訊號
QUIT:該字元使終端驅動程式向與終端相連的進程發送SIGQUIT訊號
EOF;該字元使終端驅動程式將輸入行中的全部字元傳遞給正在讀取輸入的應用程式.如果輸入行為空白,read調用將返回0,就好像在檔案尾調用read一樣
...
2.TIME和MIN值
這兩個值只用於非標準模式,兩者結合共同控制對輸入的讀取方式,還能控制在一個程式試圖與一個終端關聯的檔案描述符時將發生的情況
MIN = 0, TIME = 0時:read立即返回,如果有待處理的字元,它們就會被返回,如果沒有,read調用返回0,且不讀取任何字元
MIN = 0, TIME > 0時:有字元處理或經過TIME個0.1秒後返回
MIN > 0, TIME = 0時:read一直等待,直到有MIN個字元可以讀取,傳回值是字元的數量.到達檔案尾時返回0
MIN > 0, TIME > 0時:read調用時,它會等待接收一個字元.在接收到第一個字元及其後續的每個字元後,啟用一個字元間隔定時器.當有MIN個字元可讀或兩字元間的時間間隔超進TIME個0.1秒時,read返回
通過設定MIN和TIME值,我們可以逐個字元地對輸入進行處理
3.通過shell訪問終端模式
stty -a:這個命令用來查看當前終端的設定情況
stty sane:如果不小心設錯了終端模式,可用這個命令恢複,另一種恢複辦法是在設定之前儲存當前stty設定,在需要時再讀出
stty -g > save_stty:將當前設定儲存到檔案save_atty中
stty $(cat save_stty):讀出save_atty檔案,恢複原終端設定
第三種恢複的辦法是重新打下一個終端模擬器.查看死掉的終端進程,kill掉它
4.在命令列模式下設定終端模式
比如想讓shell指令碼讀取單個字元,就需要關閉標準模式,同時將MIN設為1,TIME設為0:
stty -icanon min1 time 0
另一個例子是關閉輸入密碼時的回顯功能:
atty -echo
使用完這個命令後要執行atty echo,將回顯功能再次恢複
5.終端速度
termios結構中沒有關於終端速度的成員和標識,但我們可以通過一組函數來實現.注意輸入和輸出是分開的,應使用不同的函數
#include <termios.h>
speed_t cfgetispeed(const struct termios *);
speed_t cfgetospeed(const struct termios *);
int cfsetispeed(struct termios *, speed_t speed);
int cfseospeed(struct termios *, speed_t speed);
這些函數只作用於termios結構,因此需要先調用tcgetattr()獲得termios結構,再調用以上函數之一設定終端速度,最後調用tcsetattr()使設定生效
上面的speed參數可設的值,其中比較重要的幾個:
B0:掛起終端
B1200:1200傳輸速率
B2400:2400傳輸速率
B9600:9600傳輸速率
B19200:19200傳輸速率
B38400:38400傳輸速率
6.其他函數
這些函數直接作用於檔案描述符,不需要讀寫termios結構:
#include <termios.h>
int tcdrain(int fd);讓調用程式一直等待,直到所有排隊的輸出都發送完畢
int tcflow(int, int flowtype);暫停或重新開始輸出
int tcflush(int fd, int in_out_selector);清空輸入,輸出或兩者都清華空
使用termios結構的密碼程式
#include <termios.h>#include <stdio.h>#include <stdlib.h>#define PASSWORD_LEN 8int main(){ struct termios initialrsettings, newrsettings; char password[PASSWORD_LEN + 1]; tcgetattr(fileno(stdin), &initialrsettings); newrsettings = initialrsettings; newrsettings.c_lflag &= ~ECHO; printf("Enter password: "); if(tcsetattr(fileno(stdin), TCSAFLUSH, &newrsettings) != 0) { fprintf(stderr,"Could not set attributes\n"); } else { fgets(password, PASSWORD_LEN, stdin); tcsetattr(fileno(stdin), TCSANOW, &initialrsettings); fprintf(stdout, "\nYou entered %s\n", password); } exit(0);}
讀取每一個字元
#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <termios.h>char *menu[] = { "a - add new record", "d - delete record", "q - quit", NULL,};int getchoice(char *greet, char *choices[], FILE *in, FILE *out);int main(){ int choice = 0; FILE *input; FILE *output; struct termios initial_settings, new_settings; if (!isatty(fileno(stdout))) { fprintf(stderr,"You are not a terminal, OK.\n"); } input = fopen("/dev/tty", "r"); output = fopen("/dev/tty", "w"); if(!input || !output) { fprintf(stderr, "Unable to open /dev/tty\n"); exit(1); } tcgetattr(fileno(input),&initial_settings); new_settings = initial_settings; new_settings.c_lflag &= ~ICANON; new_settings.c_lflag &= ~ECHO; new_settings.c_cc[VMIN] = 1; new_settings.c_cc[VTIME] = 0; new_settings.c_lflag &= ~ISIG; if(tcsetattr(fileno(input), TCSANOW, &new_settings) != 0) { fprintf(stderr,"could not set attributes\n"); } do { choice = getchoice("Please select an action", menu, input, output); printf("You have chosen: %c\n", choice); } while (choice != 'q'); tcsetattr(fileno(input),TCSANOW,&initial_settings); exit(0);}int getchoice(char *greet, char *choices[], FILE *in, FILE *out){ int chosen = 0; int selected; char **option; do { fprintf(out, "Choice: %s\n",greet); option = choices; while(*option) { fprintf(out, "%s\n",*option); option++; } do { selected = fgetc(in); } while (selected == '\n' || selected == '\r'); option = choices; while(*option) { if(selected == *option[0]) { chosen = 1; break; } option++; } if(!chosen) { fprintf(out, "Incorrect choice, select again\n"); } } while(!chosen); return selected;}