解說
Linux中的控制台界面,要大量用到Termios庫的內容。但是,總的來看,這些東西已經過時了,應該用更簡單的方案來代替它了,儘管它是POSIX標準的一部分。
以我們日常的使用情況來看,
串口已經漸漸退出了歷史舞台。現在幾乎只在
嵌入式開發群中使用(因為串口最容易出來,所以開發的第一步就是要把串口調通,這樣才能得到信息,才能方便進一步開發)。
就我們的情況來看,我們的終端基本上只與顯示系統掛鈎(也可從串口輸出,但一般只做調試用),真正更高級的互聯傳輸功能都是通過網卡通過多種協定完成的(tcp/ip是最基本的)。所以,我們在新設計的控制台實現中,可以完全簡化設計的方案,按照最基本的來設計就行了。
但是,為了做到設計出來的代碼的層次性和可移植性,Linux/Unix下終端的理論是要認真學習一下的(比如原始模式,標準模式等等),看別人是怎樣做到功能的抽象和
分離的。
NAME
termios,
tcgetattr,
tcsetattr, tcsendbreak, tcdrain, tcflush, tcflow, cfmakeraw, cfgetospeed, cfgetispeed, cfsetispeed, cfsetospeed - 獲取和設定終端
屬性,行控制,獲取和設定
波特率#include <termios.h>
#include <unistd.h>
int tcgetattr(int fd, struct termios *termios_p); //用於獲取與終端相關的參數
int
tcsetattr(int fd, int optional_actions, struct termios *termios_p); //用於設定終端參數
int tcsendbreak(int fd, int duration);
int tcdrain(int fd); //等待直到所有寫入 fd 引用的對象的輸出都被傳輸
int tcflush(int fd, int queue_selector); //刷清(扔掉)輸入快取
int tcflow(int fd, int action); //掛起傳輸或接受
int cfmakeraw(struct termios *termios_p);// 製作新的終端控制屬性
speed_t cfgetispeed(struct termios *termios_p); //得到輸入速度
speed_t cfgetospeed(struct termios *termios_p); //得到輸出速度
int cfsetispeed(struct termios *termios_p, speed_t speed); //設定輸入速度
int cfsetospeed(struct termios *termios_p, speed_t speed) //設定輸出速度
DESCRIPTION 描述
termios 函式族提供了一個常規的終端接口,用於控制非
同步通信連線埠。
這裡描述的大部分屬性有一個 termios_p 類型的參數,它是指向一個 termios 結構的
指針。這個結構包含了至少下列成員:
tcflag_t c_iflag; /* 輸入模式 */
tcflag_t c_oflag; /* 輸出模式 */
tcflag_t c_cflag; /* 控制模式 */
tcflag_t c_lflag; /* 本地模式 */
cc_t c_cc[NCCS]; /* 控制字元 */
標誌常量
IGNBRK
忽略輸入中的 BREAK 狀態。
BRKINT
如果設定了 IGNBRK,將忽略 BREAK。如果沒有設定,但是設定了 BRKINT,那么 BREAK 將使得輸入和輸出佇列被刷新,如果終端是一個前台
進程組的控制終端,這個進程組中所有進程將收到 SIGINT 信號。如果既未設定 IGNBRK 也未設定 BRKINT,BREAK 將視為與 NUL 字元同義,除非設定了 PARMRK,這種情況下它被視為序列 \377 \0 \0。
IGNPAR
忽略楨錯誤和奇偶校驗錯。
PARMRK
如果沒有設定 IGNPAR,在有奇偶校驗錯或楨錯誤的字元前插入 \377 \0。如果既沒有設定 IGNPAR 也沒有設定 PARMRK,將有奇偶校驗錯或楨錯誤的字元視為 \0。
INPCK
啟用輸入奇偶檢測。
ISTRIP
去掉第八位。
INLCR
將輸入中的 NL 翻譯為 CR。
IGNCR
忽略輸入中的回車。
ICRNL
將輸入中的回車翻譯為新行 (除非設定了 IGNCR)。
IUCLC
(不屬於 POSIX) 將輸入中的大寫字母映射為小寫字母。
IXON
IXANY
(不屬於 POSIX.1;XSI) 允許任何字元來重新開始輸出。(?)
IXOFF
啟用輸入的 XON/XOFF流控制。
IMAXBEL
(不屬於 POSIX) 當輸入佇列滿時響零。Linux 沒有實現這一位,總是將它視為已設定。
POSIX.1 中定義的 c_oflag 標誌
常量:
OPOST
啟用具體實現自行定義的輸出處理。
其餘 c_oflag 標誌常量定義在 POSIX 1003.1-2001 中,除非另外說明。
OLCUC
(不屬於 POSIX) 將輸出中的小寫字母映射為大寫字母。
ONLCR
(XSI) 將輸出中的新行符映射為回車-換行。
OCRNL
將輸出中的回車映射為新行符
ONOCR
不在第 0 列輸出回車。
ONLRET
不輸出回車。
OFILL
傳送填充字元作為延時,而不是使用定時來延時。
OFDEL
(不屬於 POSIX) 填充字元是 ASCII DEL (0177)。如果不設定,填充字元則是 ASCII NUL。
NLDLY
新行延時掩碼。取值為 NL0 和 NL1。
CRDLY
回車延時掩碼。取值為 CR0, CR1, CR2, 或 CR3。
TABDLY
水平跳格延時掩碼。取值為 TAB0, TAB1, TAB2, TAB3 (或 XTABS)。取值為 TAB3,即 XTABS,將擴展跳格為空格 (每個跳格符填充 8 個空格)。(?)
BSDLY
回退延時掩碼。取值為 BS0 或 BS1(從來沒有被實現過)。
VTDLY
豎直跳格延時掩碼。取值為 VT0 或 VT1。
FFDLY
進表延時掩碼。取值為 FF0 或 FF1。
標誌常量
CBAUD
(不屬於 POSIX)
波特率掩碼 (4+1 位)。
CBAUDEX
(不屬於 POSIX) 擴展的波特率掩碼 (1 位),包含在 CBAUD 中。
(POSIX 規定波特率存儲在 termios 結構中,並未精確指定它的位置,而是提供了函式 cfgetispeed() 和 cfsetispeed() 來存取它。一些系統使用 c_cflag 中 CBAUD 選擇的位,其他系統使用單獨的變數,例如 sg_ispeed 和 sg_ospeed 。)
CSIZE
字元長度掩碼。取值為 CS5, CS6, CS7, 或 CS8。
CSTOPB
設定兩個停止位,而不是一個。
CREAD
打開接受者。
PARENB
PARODD
HUPCL
在最後一個進程關閉設備後,降低 modem 控制線 (掛斷)。(?)
CLOCAL
忽略 modem 控制線。
LOBLK
(不屬於 POSIX) 從非當前 shell 層阻塞輸出(用於 shl )。(?)
CIBAUD
(不屬於 POSIX) 輸入速度的掩碼。CIBAUD 各位的值與 CBAUD 各位相同,左移了 IBSHIFT 位。
CRTSCTS
(不屬於 POSIX) 啟用 RTS/CTS (硬體)
流控制。
標誌常量
ISIG
當接受到字元 INTR, QUIT, SUSP, 或 DSUSP 時,產生相應的信號。
ICANON
啟用標準模式 (canonical mode)。允許使用特殊字元 EOF, EOL, EOL2, ERASE, KILL, LNEXT, REPRINT, STATUS, 和 WERASE,以及按行的緩衝。
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 的 ASCII 碼。例如,字元 0x08 (BS) 被回顯為 ^H。
ECHOPRT
(不屬於 POSIX) 如果同時設定了 ICANON 和 IECHO,字元在刪除的同時被列印。
ECHOKE
(不屬於 POSIX) 如果同時設定了 ICANON,回顯KILL 時將刪除一行中的每個字元,如同指定了 ECHOE 和 ECHOPRT 一樣。
DEFECHO
(不屬於 POSIX) 只在一個進程讀的時候回顯。
FLUSHO
(不屬於 POSIX; Linux 下不被支持) 輸出被刷新。這個標誌可以通過鍵入字元
DISCARD來開關。
NOFLSH
禁止在產生 SIGINT, SIGQUIT 和 SIGSUSP 信號時刷新輸入和輸出佇列。
TOSTOP
向試圖寫控制終端的後台進程組傳送 SIGTTOU 信號。
PENDIN
(不屬於 POSIX; Linux 下不被支持) 在讀入下一個字元時,輸入佇列中所有字元被重新輸出。(bash 用它來處理 typeahead)
IEXTEN
啟用實現自定義的輸入處理。這個標誌必須與 ICANON 同時使用,才能解釋特殊字元 EOL2,LNEXT,REPRINT 和 WERASE,IUCLC 標誌才有效。
c_cc
數組定義了特殊的
控制字元。符號下標 (初始值) 和意義為:
VINTR
(003, ETX, Ctrl-C, or also 0177, DEL, rubout) 中斷字元。發出 SIGINT 信號。當設定 ISIG 時可被識別,不再作為輸入傳遞。
VQUIT
(034, FS, Ctrl-\) 退出字元。發出 SIGQUIT 信號。當設定 ISIG 時可被識別,不再作為輸入傳遞。
VERASE
(0177, DEL, rubout, or 010, BS, Ctrl-H, or also #) 刪除字元。刪除上一個還沒有刪掉的字元,但不刪除上一個 EOF 或行首。當設定 ICANON 時可被識別,不再作為輸入傳遞。
VKILL
(025, NAK, Ctrl-U, or Ctrl-X, or also @) 終止字元。刪除自上一個 EOF 或行首以來的輸入。當設定 ICANON 時可被識別,不再作為輸入傳遞。
VEOF
(004, EOT, Ctrl-D) 檔案尾字元。更精確地說,這個字元使得 tty 緩衝中的內容被送到等待輸入的
用戶程式中,而不必等到 EOL。如果它是一行的第一個字元,那么用戶程式的 read() 將返回 0,指示讀到了 EOF。當設定 ICANON 時可被識別,不再作為輸入傳遞。
VMIN
非 canonical 模式讀的最小字元數。
VEOL
(0, NUL) 附加的行尾字元。當設定 ICANON 時可被識別。
VTIME
非 canonical 模式讀時的延時,以十分之一秒為單位。
VEOL2
(not in POSIX; 0, NUL) 另一個行尾字元。當設定 ICANON 時可被識別。
VSWTCH
(not in POSIX; not supported under Linux; 0, NUL) 開關字元。(只為 shl 所用。)
VSTART
(021, DC1, Ctrl-Q) 開始字元。重新開始被 Stop 字元中止的輸出。當設定 IXON 時可被識別,不再作為輸入傳遞。
VSTOP
(023, DC3, Ctrl-S) 停止字元。停止輸出,直到鍵入 Start 字元。當設定 IXON 時可被識別,不再作為輸入傳遞。
VSUSP
(032, SUB, Ctrl-Z) 掛起字元。傳送 SIGTSTP 信號。當設定 ISIG 時可被識別,不再作為輸入傳遞。
VDSUSP
(not in POSIX; not supported under Linux; 031, EM, Ctrl-Y) 延時掛起信號。當用戶程式讀到這個字元時,傳送 SIGTSTP 信號。當設定 IEXTEN 和 ISIG,並且系統支持作業管理時可被識別,不再作為輸入傳遞。
VLNEXT
(not in POSIX; 026, SYN, Ctrl-V) 字面上的下一個。引用下一個輸入字元,取消它的任何特殊含義。當設定 IEXTEN 時可被識別,不再作為輸入傳遞。
VWERASE
(not in POSIX; 027, ETB, Ctrl-W) 刪除詞。當設定 ICANON 和 IEXTEN 時可被識別,不再作為輸入傳遞。
VREPRINT
(not in POSIX; 022, DC2, Ctrl-R) 重新輸出未讀的字元。當設定 ICANON 和 IEXTEN 時可被識別,不再作為輸入傳遞。
VDISCARD
(not in POSIX; not supported under Linux; 017, SI, Ctrl-O) 開關:開始/結束丟棄未完成的輸出。當設定 IEXTEN 時可被識別,不再作為輸入傳遞。
VSTATUS
(not in POSIX; not supported under Linux; status request: 024, DC4, Ctrl-T).
這些符號下標值是互不相同的,除了 VTIME,VMIN 的值可能分別與 VEOL,VEOF 相同。 (在 non-canonical 模式下,特殊字元的含義更改為延時含義。MIN 表示應當被讀入的最小
字元數。TIME 是以十分之一秒為單位的計時器。如果同時設定了它們,read 將等待直到至少讀入一個字元,一旦讀入 MIN 個字元或者從上次讀入字元開始經過了 TIME 時間就立即返回。如果只設定了 MIN,read 在讀入 MIN 個字元之前不會返回。如果只設定了 TIME,read 將在至少讀入一個字元,或者計時器逾時的時候立即返回。如果都沒有設定,read 將立即返回,只給出當前準備好的字元。) (?)
tcgetattr() 得到與 fd 指向的對象相關的參數,將它們保存於 termios_p 引用的 termios 結構中。函式可以從後台進程中調用;但是,終端屬性可能被後來的前台進程所改變。
tcsetattr() 設定與終端相關的參數(除非需要底層支持卻無法滿足),使用 termios_p 引用的 termios 結構。optional_actions 指定了什麼時候改變會起作用:
TCSANOW
改變立即發生
TCSADRAIN
改變在所有寫入 fd 的輸出都被傳輸後生效。這個函式應當用於修改影響輸出的參數時使用。
TCSAFLUSH
改變在所有寫入 fd 引用的對象的輸出都被傳輸後生效,所有已接受但未讀入的輸入都在改變發生前丟棄。
tcsendbreak() 傳送連續的 0 值比特流,持續一段時間,如果終端使用異步串列數據傳輸的話。如果 duration 是 0,它至少傳輸 0.25 秒,不會超過 0.5 秒。如果 duration 非零,它傳送的時間長度由實現定義。
如果終端並非使用異步串列數據傳輸,tcsendbreak() 什麼都不做。
tcdrain() 等待直到所有寫入 fd 引用的對象的輸出都被傳輸。
tcflush() 丟棄要寫入 引用的對象,但是尚未傳輸的數據,或者收到但是尚未讀取的數據,取決於 queue_selector 的值:
TCIFLUSH
刷新收到的數據但是不讀
TCOFLUSH
刷新寫入的數據但是不傳送
TCIOFLUSH
同時刷新收到的數據但是不讀,並且刷新寫入的數據但是不傳送
tcflow() 掛起 fd 引用的對象上的數據傳輸或接收,取決於 action 的值:
TCOOFF
掛起輸出
TCOON
重新開始被掛起的輸出
TCIOFF
傳送一個 STOP 字元,停止終端設備向系統傳送數據
TCION
傳送一個 START 字元,使終端設備向系統傳輸數據
打開一個終端設備時的默認設定是輸入和輸出都沒有掛起。
波特率函式被用來獲取和設定 termios 結構中,輸入和輸出波特率的值。新值不會馬上生效,直到成功調用了
tcsetattr() 函式。
輸入和輸出
波特率被保存於 termios 結構中。
cfmakeraw 設定終端屬性如下:
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;
cfgetospeed() 返回 termios_p 指向的 termios 結構中存儲的輸出波特率
cfsetospeed() 設定 termios_p 指向的 termios 結構中存儲的輸出波特率為 speed。取值必須是以下
常量之一:
B0 B50 B75 B110 B134 B150 B200 B300 B600 B1200 B1800 B2400 B4800 B9600 B19200 B38400 B57600 B115200 B230400
零值 B0 用來中斷連線。如果指定了 B0,不應當再假定存在連線。通常,這樣將下線。CBAUDEX 是一個掩碼,指示高於 POSIX.1 定義的速度的那一些 (57600 及以上)。因此,B57600 & CBAUDEX 為非零。
cfsetispeed() 設定 termios 結構中存儲的輸入波特率為 speed。如果輸入波特率被設為0,實際輸入波特率將等於輸出波特率。
返回值
cfgetispeed() 返回 termios 結構中存儲的輸入波特率。
cfgetospeed() 返回 termios 結構中存儲的輸出波特率。
其他函式返回:
0
成功
-1
失敗,並且為 errno 置值來指示錯誤。
注意tcsetattr() 返回成功,如果任何所要求的修改可以實現的話。因此,當進行多重修改時,應當在這個函式之後再次調用tcgetattr() 來檢測是否所有修改都成功實現。
注意
Unix V7 以及很多後來的系統有一個波特率的列表,在十四個值 B0, ..., B9600 之後可以看到兩個常數 EXTA, EXTB ("External A" and "External B")。很多系統將這個列表擴展為更高的波特率。
tcsendbreak 中非零的 duration 有不同的效果。SunOS 指定中斷 duration*N 秒,其中 N 至少為 0.25,不高於 0.5 。Linux, AIX, DU, Tru64 傳送 duration 微秒的 break 。FreeBSD, NetBSD, HP-UX 以及 MacOS 忽略 duration 的值。在 Solaris 和 Unixware 中, tcsendbreak 搭配非零的 duration 效果類似於 tcdrain。
SEE ALSO 參見