-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功能測試宏定義:
2. termios結構體
大多數termios函數都會用到termios結構。termios結構體定義如下:
- typedef unsigned char cc_t;
- typedef unsigned int speed_t;
- typedef unsigned int tcflag_t;
- struct termios
- {
- tcflag_t c_iflag; /* input mode flags */
- tcflag_t c_oflag; /* output mode flags */
- tcflag_t c_cflag; /* control mode flags */
- tcflag_t c_lflag; /* local mode flags */
- cc_t c_line; /* line discipline */
- cc_t c_cc[NCCS]; /* control characters */
- speed_t c_ispeed; /* input speed */
- speed_t c_ospeed; /* output speed */
- #define _HAVE_STRUCT_TERMIOS_C_ISPEED 1
- #define _HAVE_STRUCT_TERMIOS_C_OSPEED 1
- };
c_iflag標誌常量
- IGNBRK 忽略輸入BREAK條件
- BRKINT 如果設置了IGNBRK,BREAK將會被忽略;如果僅僅設置了BRKINT,BREAK將會丟棄輸入和輸出隊列中的數據(flush),並且如果終端爲前臺進程組的控制終端,則BREAK將會產生一個SIGINT信號發送到這個前臺進程組。如果IGNBRK和BRKINT均未設置,BREAK將會被當作讀入了null字節('\0'),除非設置了PARMRK標誌被設置,在這種情況下,BREAK將會被當作讀入了\377\0\0序列。
- IGNPAR 忽略幀錯誤和奇偶校驗錯誤
- PARMRK 如果未設置IGNPAR標誌,在帶有奇偶校驗錯誤或幀錯誤的字符前使用\377\0來標誌;如果IGNPAR和PARMRK均未設置,則將奇偶校驗錯誤的字符當作\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)如果同時設置有ICANON和IECHO,字符以已刪除方式打印
- 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模式下,無需用戶輸入行定界符,輸入立即可讀取。
- 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模式下,終端屬性如下:
- termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
- | INLCR | IGNCR | ICRNL | IXON);
- termios_p->c_oflag &= ~OPOST;
- termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
- termios_p->c_cflag &= ~(CSIZE | PARENB);
- 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. 線速
控制輸入/輸出波特率
- 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輸入。
- #include <stdio.h>
- #include <unistd.h>
- #include <termios.h>
- #include <errno.h>
- #include <string.h>
- #include <stdlib.h>
- #define BUF_LENGTH 255
- int main(void)
- {
- int ret = 0;
- char buf[BUF_LENGTH]={0};
- int i = 0;
- struct termios newtmios={0};
- struct termios oldtmios={0};
- ret = tcgetattr(STDIN_FILENO, &oldtmios);
- if ( ret )
- {
- printf("tcgetattr() error, errno = 0x%X\n", errno);
- return -1;
- }
- memcpy(&newtmios, &oldtmios, sizeof(struct termios));
- newtmios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
- | INLCR | IGNCR | ICRNL | IXON);
- newtmios.c_oflag &= ~OPOST;
- newtmios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
- newtmios.c_cflag &= ~(CSIZE | PARENB);
- newtmios.c_cflag |= CS8;
- ret = tcsetattr(STDIN_FILENO, TCSANOW, &newtmios);
- if ( ret )
- {
- printf("tcsetattr() error, errno = 0x%X\n", errno);
- return -2;
- }
- printf("Press any key(Ctrl-b to quit) :\n ");
- tcdrain(STDIN_FILENO);
- while ( 1 )
- {
- ret = read(STDIN_FILENO, buf, BUF_LENGTH);
- if ( ret )
- {
- printf("\rread %d chars, you pressed ", ret);
- for(i=0; i<ret;i++) printf(" 0x%02X ", buf[i]);
- printf("\r\n");
- if ( (1==ret) && (0x02 == buf[0]) )
- {
- break;
- }
- }
- }
- ret = tcsetattr(STDIN_FILENO, TCSANOW, &oldtmios);
- return ret;
- }