原文鏈接termios結構
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);
<span style="font-family:SimSun;font-size:18px;">#include<termios.h>
int tcgetattr(int fd, struct termios *termios_p);</span>
這個函數調用把當前終端接口變量的值寫入termios_p參數指向的結構。如果這些值其後被修改了,你可以通過調用函數tcsetattr來重新配置
終端接口。
- #include<termios.h>
- int tcsetattr(int fd , int actions , const struct termios *termios_h);
<span style="font-family:SimSun;font-size:18px;">#include<termios.h>
int tcsetattr(int fd , int actions , const struct termios *termios_h);</span>
參數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 8
- int 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);
- }
<span style="font-family:SimSun;font-size:18px;">#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#define PASSWORD_LEN 8
int 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);
}
</span>
讀取每一個字符
- #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;
- }