termios 系列函數分析

-tcgetattr, tcsetattr, tcsendbreak, tcdrain, tcflush, tcflow, cfmakeraw, cfgetospeed, cfgetispeed, cfsetispeed, cfsetospeed, cfsetspeed等,用以獲取/設置終端設備的屬性/控制/速度。

1. 函數聲明

函數聲明

#include <termios.h>
#include <unistd.h>
/*獲取文件描述符fd對應設備狀態置入termios_p所指向結構體中*/
int tcgetattr(int fd, struct termios *termios_p);
/*設置文件描述符對應設備狀態*/
int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);
/*向fd發送0比特*/
int tcsendbreak(int fd, int duration);
/*掛起直到所有寫入fd的輸出全部發送完畢*/
int tcdrain(int fd);
/*丟棄所有準備寫入但還未發送給fd的數據或從fd已接收但還還未被讀取的數據*/
/*丟棄對象取決於queue_selector*/
int tcflush(int fd, int queue_selector);
/*掛起fd發送操作或接收操作,掛起對象取決於action*/
int tcflow(int fd, int action);
/*設備終端屬性*/
void cfmakeraw(struct termios *termios_p);
/*返回termios_p所指向結構體中的輸入波特率*/
speed_t cfgetispeed(const struct termios *termios_p);
/*返回termios_p所指向結構體中的輸出波特率*/
speed_t cfgetospeed(const struct termios *termios_p);
/*設置termios_p所指向結構體中的輸入波特率*/
int cfsetispeed(struct termios *termios_p, speed_t speed);
/*設置termios_p所指向結構體中的輸出波特率*/
int cfsetospeed(struct termios *termios_p, speed_t speed);
/*4.4BSD擴展,設置輸入輸出波特率*/
int cfsetspeed(struct termios *termios_p, speed_t speed);

glibc功能測試宏定義:

cfsetspeed(), cfmakeraw(): _BSD_SOURCE

2. termios結構體

大多數termios函數都會用到termios結構。termios結構體定義如下:

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. typedef unsigned char   cc_t;  
  2. typedef unsigned int    speed_t;  
  3. typedef unsigned int    tcflag_t;  
  4.   
  5.   
  6. struct termios  
  7. {  
  8.     tcflag_t c_iflag;       /* input mode flags */  
  9.     tcflag_t c_oflag;       /* output mode flags */  
  10.     tcflag_t c_cflag;       /* control mode flags */  
  11.     tcflag_t c_lflag;       /* local mode flags */  
  12.     cc_t c_line;            /* line discipline */  
  13.     cc_t c_cc[NCCS];        /* control characters */  
  14.     speed_t c_ispeed;       /* input speed */  
  15.     speed_t c_ospeed;       /* output speed */  
  16. #define _HAVE_STRUCT_TERMIOS_C_ISPEED 1  
  17. #define _HAVE_STRUCT_TERMIOS_C_OSPEED 1  
  18. };  

c_iflag標誌常量

  • IGNBRK 忽略輸入BREAK條件
  • BRKINT 如果設置了IGNBRK,BREAK將會被忽略;如果僅僅設置了BRKINT,BREAK將會丟棄輸入和輸出隊列中的數據(flush),並且如果終端爲前臺進程組的控制終端,則BREAK將會產生一個SIGINT信號發送到這個前臺進程組。如果IGNBRKBRKINT均未設置,BREAK將會被當作讀入了null字節('\0'),除非設置了PARMRK標誌被設置,在這種情況下,BREAK將會被當作讀入了\377\0\0序列。
  • IGNPAR 忽略幀錯誤和奇偶校驗錯誤
  • PARMRK 如果未設置IGNPAR標誌,在帶有奇偶校驗錯誤或幀錯誤的字符前使用\377\0來標誌;如果IGNPARPARMRK均未設置,則將奇偶校驗錯誤的字符當作\0
  • INPCK 允許輸入奇偶校驗
  • ISTRIP 剝離第8個bit
  • INLCR 將輸入中的NL轉換成CR
  • IGNCR 忽略輸入中的回車
  • ICRNL 將輸入中的CR轉換爲NL
  • IUCLC (非POSIX)將輸入中的大寫字符轉換爲小寫
  • IXON 允許輸出端的XON/XOFF流控
  • IXANY (XSI)任意擊鍵將會重啓已停止的輸出(默認情況僅允許使用START字符來重啓輸出)
  • IXOFF 允許輸入端XON/XOFF流控
  • IMAXBEL (非POSIX)輸入隊列滿時響鈴。Linux未實現此標誌位,總是以此標誌位被設置的情況動作
  • IUTF8 (從Linux 2.6.4開始支持,非POSIX)輸入爲UTF8編碼

c_oflag標誌常量定義(POSIX.1):

  • OPOST 允許實現定義的輸出處理
  • OLCUC (非POSIX)將輸出中的小寫字母映射爲大寫
  • ONLCR (XSI)將輸出中的NL映射爲CR-NL
  • OCRNL 將輸出中的CR映射爲NL
  • ONOCR 不在第零列輸出CR
  • ONLRET 不輸出CR
  • OFILL 發送填充字符實現延遲,而不是使用時間上的延遲
  • OFDEL (非POSIX)填充字符爲ASCII DEL(0177)。如果未設置,填充字符爲ASCII NUL('\0')。(Linux中未實現)
  • NLDLY 新行延遲掩碼。值爲NL0和NL1。(需要_BSD_SOURCE或_SVID_SOURCE或_XOPEN_SOURCE)
  • CRDLY 回車(CR)延遲掩碼。值爲CR0,CR1,CR2,或CR3。(需要_BSD_SOURCE或_SVID_SOURCE或_XOPEN_SOURCE)
  • TABDLY 水平製表符延遲掩碼。值爲TAB0,TAB1,TAB2,TAB3(或XTABS)。值TAB3/XTABS表示將製表符擴展爲空格(每8列爲一個製表符停止位)。(需要_BSD_SOURCE或_SVID_SOURCE或_XOPEN_SOURCE)
  • BSDLY 回退符延遲掩碼。值爲BS0或BS1.(從未實現)(需要_BSD_SOURCE或_SVID_SOURCE或_XOPEN_SOURCE)
  • VTDLY 垂直製表符延遲掩碼。值爲VT0或VT1。
  • FFDLY 表單輸入延遲掩碼。值爲FF0或FF1.(需要_BSD_SOURCE或_SVID_SOURCE或_XOPEN_SOURCE)

c_cflag標誌常量:

  • CBAUD (非POSIX)波特速率掩碼(4+1比特)。(需要_BSD_SOURCE或_SVID_SOURCE或_XOPEN_SOURCE)
  • CBAUDEX (非POSIX)附加波特速率掩碼(1比特),包含在CBAUD中。(需要_BSD_SOURCE或_SVID_SOURCE或_XOPEN_SOURCE)
  • CSIZE 字符尺寸掩碼。值爲CS5,CS6,CS7,或CS8
  • CSTOPB 設置兩個停止位(bits),而不是一位
  • CREAD 允許接收器
  • PARENB 允許輸出端生產奇偶校驗位,輸入端進行校驗
  • PARODD 如設置,則輸入輸出端奇偶校驗爲奇校驗;未設置則爲偶校驗
  • HUPCL 上一次操作關閉設備後將調制解調器控制線設爲低電平(掛起)
  • CLOCAL 忽略調制解調器控制線
  • LOBLK (非POSIX)阻塞非當前shell層輸出。(Linux未實現)
  • CIBAUD (非POSIX)輸入速率掩碼。
  • CMSPAR (非POSIX)使用"stick"奇偶校驗:如果設置了PARODD,將奇偶檢驗位總是置爲1;如果未設置PARODD,奇偶校驗位總是置爲0.
  • CRTSCTS 允許RTS/CTS(硬件)流控。(需要_BSD_SOURCE或_SVID_SOURCE)

c_lflag標誌常量:

  • ISIG 當接收到INTR/QUIT/SUSP/DSUSP字符,生成一個相應的信號
  • ICANON 允許canonical模式
  • XCASE (非POSIX,Linux不支持)如果設置了ICANON,終端僅爲大寫字符模式。輸入字符被轉換爲小寫,除非以'\'開始;輸出端,大寫字符以'\'開始,小寫字符被轉換爲大寫
  • ECHO 回顯所輸入的字符
  • ECHOE 如果同時設置了ICANON標誌,ERASE字符刪除前一個所輸入的字符,WERASE刪除前一個輸入的單詞
  • ECHOK 如果同時設置有ICANON標誌,KILL字符刪除當前行
  • ECHONL 如果同時設置有ICANON標誌,回顯NL字符即使ECHO未設置
  • ECHOCTL (非POSIX)如果同時設置有ECHO標誌,除TAB/NL/START/STOP外的ASCII控制字符將會被回顯成'^X',其中X爲控制符數值加0x40
  • ECHOPRT (非POSIX)如果同時設置有ICANONIECHO,字符以已刪除方式打印
  • ECHOKE (非POSIX)如果設置有ICANON,KILL以刪除所在行所有字符方式顯示
  • DEFECHO (非POSIX)僅當有進程讀取時回顯字符(Linux未實現)
  • FLUSHO (非POSIX,LINUX不支持)輸出被丟棄。
  • NOFLSH 當生成SIGINT/SIGQUIT/SIGSUSP信號時禁止丟棄(flush)輸入輸出隊列
  • TOSTOP 當後臺進程試圖寫入自己的控制終端時,發送SIGTTOU信號給進程組
  • PENDIN (非POSIX,Linux不支持)
  • IEXTEN 允許實現所定義的輸入處理。

c_cc數組定義了一些特殊控制字符:

  • VINTR 003,ETX,Ctrl-C,0177,DEL,rubout,中斷字符。發送SIGINT信號。
  • VQUIT 034,FS,Ctrl-\,退出字符。發送SIGQUIT信號。
  • VERASE 0177,DEL,rubout,010,BS,Ctrl-H,#, 刪除字符。刪除上一個未刪除的字符,但不刪除前面的EOF或者行開始字符。
  • VKILL 025,NAK,Ctrl-U,Cgtrl-X,@,Kill字符。刪除自上一個EOF或行開始字符之後的所有輸入字符。
  • VEOF 004,EOT,Ctrl-D,文件結尾(End-of-file)。EOF將會讓掛起的tty緩衝區內容發送給處於等待中的用戶程序,而不用等待行結束標識(End-of-line)。
  • VMIN 非canonical模式讀操作的最少字符數
  • VEOL (0,NUL)額外的行結束符(End-of-line)
  • VTIME 非canonical模式讀操作超時(單位爲1/10秒)
  • VEOL2 (非POSIX;0,NUL)另一個行結束標識
  • VSWTCH (非POSIX;Linux不支持;0,NUL)切換字符
  • VSTART 021,DC1,Ctrl-Q,開始字符。重啓被STOP字符停止的輸出
  • VSTOP 023,DC3,Ctrl-S,停止字符。停止輸出直到START
  • VSUSP 032,SUB,Ctrl-Z,掛起字符。發送SIGSTP信號
  • VDSUSP (非POSIX;Linux不支持)031,EM,Ctrl-Y,延遲掛起字符:當用戶程序讀取字符時發送SIGTSTP信號
  • VLNEXT (非POSIX)026,SYN,Ctrl-V,標識下一個字符爲字面意思而非可能的特殊控制含義
  • VWERASE (非POSIX)027,ETC,Ctrl-W,單詞刪除
  • VREPRINT (非POSIX)022,DC2,Ctrl-R,再次打印未讀取字符
  • VDISCARD (非POSIX;Linux不支持)017,SI,Ctrl-O,開關切換:開始/停止丟棄掛起的輸出
  • VSTATUS (非POSIX;Linux不支持)024,DC4,Ctrl-T,狀態請求

3. 獲取/更改終端設置

tcgetattr()tcsetattr()分別用於獲取/更改終端設置

  • tcgetattr() 獲取fd所指定終端的設置並存放入termios結構指針termios_p指向的地址空間;後臺進程所獲取的終端設置也可能隨後被前臺進程更改
  • tcsetattr() 設置指定終端的屬性。可選動作項指定終端屬性何時更改:
    • TCSANOW 立即更改
    • TCSADRAIN 當寫入fd的所有輸出發送完畢後更改
    • TCSAFLUSH 所有寫入fd的輸出發送完畢,並且所有已接收但未讀入的輸入被丟棄後更改設置

4. Canonical和non-canonical模式

c_lflag字段中的ICANON標誌決定終端是否工作在canonical模式。缺省情況下,終端爲canonical模式

canonical模式

  • 輸入工作在行模式。收到行定界符(NL,EOL,EOL2;或行首的EOF)後,輸入行可供讀取。read操作所讀取的行內容包含行定界符。
  • 允許行編輯(ERASE,KILL;如設置了IEXTEN標誌,WERASE,REPRINT,LNEXT)。

non-canonical模式下,無需用戶輸入行定界符,輸入立即可讀取。

c_cc[VTIME]和c_cc[VMIN]對read操作的影響:

  • MIN==0;TIME==0:如有數據可用,read立即返回min(請求數量,可用字符數量)個字符;如無數據可用,read返回0
  • MIN>0;TIME==0:read阻塞,直到至少有min(請求數量,MIN)個字符可用,read返回兩值中較小的一個
  • MIN==0;TIME>0:TIME指定讀取超時(單位爲1/10秒)。當調用read時設定定時器。當至少有一個字符可用或超時後,read返回。如果在超時前無可用字符,read返回0
  • MIN>0;TIME>0:TIME指定讀取超時,收到輸入的第一個字符後重啓定時器。read在讀取到MIN和所請求數量兩者中較少的字符,或超時後,返回。至少會讀取到一個字符。

5. Raw模式

cfmakeraw()設置終端工作在"raw"模式下:輸入以字符方式提供,禁止回顯,所有特殊字符被禁止。

raw模式下,終端屬性如下:

  1. termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP  
  2.                       | INLCR | IGNCR | ICRNL | IXON);  
  3. termios_p->c_oflag &= ~OPOST;  
  4. termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);  
  5. termios_p->c_cflag &= ~(CSIZE | PARENB);  
  6. termios_p->c_cflag |= CS8;  

6. 線控

控制與終端的連接

  • tcsendbreak 如果終端工作在異步串行數據發送模式,在指定時長內發送0比特流。如果時長爲0,發送0比特流至少0.25s,但不多於0.5s。如果終端未工作在異步串行數據發送模式,tcsendbreak立刻返回不作任何操作。
  • tcdrain() 等待直到所有輸出至fd的數據均被髮送
  • tcflush() 丟棄已寫入fd但還未發送的數據,或丟棄已接收但還未讀取的數據。丟棄對象取決於queue_selector:
    • TCIFLUSH 丟棄已接收但還未讀取的數據
    • TCOFLUSH 丟棄已寫入但還未發送的數據
    • TCIOFLUSH 丟棄以上兩種
  • tcflow() 掛起fd的發送或接收操作。掛起對象取決於action:
    • TCOOFF 掛起輸出
    • TCOON 重啓掛起的輸出
    • TCIOFF 發送STOP字符,停止終端設備向系統發送數據
    • TCION 發送START字符,啓動設備向系統發送數據

7. 線速

控制輸入/輸出波特率

設置波特率爲B0會使調制解調器掛起(Hang up)。B38400對應的實際速率可能會受到setserial(8)的影響。

  • cfgetospeed()返回輸出波特率
  • cfsetospeed()設置輸出波特率爲B0/B50/B75/B110/B134/B150/B200/B300/B600/B1200/B1800/B2400/B4800/B9600/B38400/B57600/B115200/B230400
    *B0用於終止連接
  • cfgetispeed() 返回輸入波特率
  • cfsetispeed() 設置輸入波特率
  • cfsetspeed() 4.4BSD擴展,同時將輸入輸入波特率設置爲同一值

8. 返回值

標識函數調用結果

  • cfgetispeed()/cfgetospeed() 返回輸入/輸出波特率
  • 對其他函數,返回值爲0表示成功;-1表示失敗,errno給出錯誤
    *對於tcsetattr(),只要任一要更改的屬性設置成功,則會返回成功。

9. 例

獲取標準輸入終端(STDIN)的屬性並更改標準輸入終端爲raw模式(也可以使用更簡便的調用-cfmakeraw()完成),接收鍵盤輸入、顯示鍵值,直到接收到Ctrl-b輸入。

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3. #include <termios.h>  
  4. #include <errno.h>  
  5. #include <string.h>  
  6. #include <stdlib.h>  
  7.   
  8. #define BUF_LENGTH 255  
  9.   
  10. int main(void)  
  11. {  
  12.     int ret = 0;  
  13.     char buf[BUF_LENGTH]={0};  
  14.     int i = 0;  
  15.   
  16.     struct termios newtmios={0};  
  17.     struct termios oldtmios={0};  
  18.   
  19.     ret = tcgetattr(STDIN_FILENO, &oldtmios);  
  20.     if ( ret )  
  21.     {  
  22.         printf("tcgetattr() error, errno = 0x%X\n", errno);  
  23.         return -1;  
  24.     }  
  25.   
  26.     memcpy(&newtmios, &oldtmios, sizeof(struct termios));  
  27.     newtmios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP  
  28.                 | INLCR | IGNCR | ICRNL | IXON);  
  29.     newtmios.c_oflag &= ~OPOST;  
  30.     newtmios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);  
  31.     newtmios.c_cflag &= ~(CSIZE | PARENB);  
  32.     newtmios.c_cflag |= CS8;  
  33.   
  34.     ret = tcsetattr(STDIN_FILENO, TCSANOW, &newtmios);  
  35.     if ( ret )  
  36.     {  
  37.         printf("tcsetattr() error, errno = 0x%X\n", errno);  
  38.         return -2;  
  39.     }  
  40.   
  41.     printf("Press any key(Ctrl-b to quit) :\n ");  
  42.     tcdrain(STDIN_FILENO);  
  43.     while ( 1 )  
  44.     {  
  45.         ret = read(STDIN_FILENO, buf, BUF_LENGTH);  
  46.         if ( ret )  
  47.         {  
  48.             printf("\rread %d chars, you pressed ", ret);  
  49.             for(i=0; i<ret;i++) printf(" 0x%02X ", buf[i]);  
  50.             printf("\r\n");  
  51.   
  52.             if ( (1==ret) && (0x02 == buf[0]) )  
  53.             {  
  54.                 break;  
  55.             }  
  56.         }  
  57.     }  
  58.   
  59.     ret = tcsetattr(STDIN_FILENO, TCSANOW, &oldtmios);  
  60.     return ret;  
  61. }  
發佈了28 篇原創文章 · 獲贊 17 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章