衆所周知,在Linux系統下全部設備都是以文件的形式存在,串口也同樣。linux
一般I/O操做都是有阻塞與非阻塞的兩種方式。ios
其中"超時"這個概念實際上是阻塞中的一種處理手段,本質仍是屬於阻塞的I/O模式.編程
在Linux中串口的IO操做 本文將它分爲三種狀態:ubuntu
阻塞狀態ide
超時狀態函數
非阻塞狀態spa
這三種狀態的轉換組合有這麼幾種:.net
阻塞 --> 超時code
阻塞 --> 非阻塞blog
超時 --> 阻塞
超時 --> 非阻塞
非阻塞 --> 阻塞
咱們一個一個來分析
首先在一個串口的描述符打開的時候指定它的模式是阻塞仍是阻塞
引用《UNIX環境高級編程》(第二版):
O_NOCTTY:O_NOCTTY 若是p a t h n a m e指的是終端設備,則不將此設備分配做爲此進程的控制終端
較早的系統 V版本引入了 O _ N D E L AY(不延遲)標誌,它與 O _ N O N B L O C K
(不阻塞)選擇項相似,但在讀操做的返回值中具備兩義性。若是不能從管道、
F I F O或設備讀得數據,則不延遲選擇項使 r e a d返回0,這與表示已讀到文件尾端的
返回值0相沖突。 S V R 4仍支持這種語義的不延遲選擇項,可是新的應用程序應當
使用不阻塞選擇項以代替之。
當一個串口是阻塞狀態的時候即可以設置它爲超時狀態。
利用 struct termios 的 cc_t c_cc[NCCS] 成員
如需須要設置超時則c_cc[VMIN] 必須等於0
這表明可以讀取的最小字符是0個,即便用read讀取數據超時read返回0
當c_cc[VTIME] 設置爲 0 且 c_cc[VMIN] == 0 的時候,表明超時0秒(姑且這麼叫吧!)
這個時候使用read讀取數據會當即返回(有讀到數據時返回字節數,沒有數據和通常超時同樣返回0)
可是!
雖然這時候在現象上看起來和非阻塞模式同樣(read都不會阻塞)但返回值不一樣
補充:在非阻塞模式下修改c_cc[VMIN] 和 c_cc[VTIME] 的狀況
若在非阻塞模式下修改
c_cc[VMIN]爲0而且c_cc[VTIME]也爲0時read無數據會返回 0 (現象同"超時0秒"同樣)
這時假若將c_cc[VMIN]或者c_cc[VTIME]中任意一個項修改爲>0,那麼read就返回-1了。
雖然表現形式同樣,但在編程時必需要了解本身使用的是哪種模式和串口當前的狀態才能更好的分析和處理問題。
這裏說一下我曾經遇到過的一個問題:
我在打開串口時使用阻塞模式打開,可是沒有設置c_cc[VMIN]的值,而它初始化後就是0,因此發現串口沒有被阻塞,其實緣由就是串口模式仍是阻塞模式沒錯,可是它是超時0秒的狀態,因此在沒有數據到達時read也返回了。
關於阻塞模式下c_cc[VMIN] 和 c_cc[VTIME]的取值與現象,如下簡稱爲VMIN和VTIME
這兩個值有這些組合
VTIME | VMIN | 說明 |
0 | 0 | "超時0秒" |
0 | >0 | 一直阻塞到接收到VMIN個數據時read返回 |
>0 | 0 | 普通超時 |
>0 | >0 | 當接收到第一個字節時開始計算超時。 若是超時時間未到但數據已經達到VMIN個read當即返回。 若是超時時間到了就返回當前讀到的個數。 |
flag = fcntl(fd,F_GETFL); //先獲取原文件狀態(假定函數執行成功返回文件狀態) flag &= ~O_NONBLOCK; //去除非阻塞的flag fcntl(fd,F_SETFL,flag); //設置新的文件狀態
使用以上的方式可轉換成阻塞狀態,這時就能夠能夠進行超時設置了。
若是在非阻塞狀態已經設置了超時時,在轉換成阻塞狀態後超時隨之生效。
flag = fcntl(fd,F_GETFL); //先獲取原文件狀態(假定函數執行成功返回文件狀態) flag |= O_NONBLOCK; //增長阻塞的flag fcntl(fd,F_SETFL,flag); //設置新的文件狀態
使用以上的方式可轉換成非阻塞狀態,超時效果失效。
也可使用如下方法設置成非阻塞狀態:
fcntl(fd,F_SETFL,FNDELAY); //有些代碼中使用 fcntl(fd,F_SETFL,FNONBLOCK);
這裏提一下 <fcntl.h>頭文件中幾個宏的定義
gcc 版本 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)
能夠得出一下結論:
O_NDELAY == O_NONBLOCK == 0x4000
FNONBLOCK == O_NONBLOCK
FNDELAY == O_NDELAY
因此 FNDELAY == FNONBLOCK
上面設置非阻塞模式的方法歸根結底就是要把文件狀態加上O_NONBLOCK一個非阻塞的標誌。
若是在非阻塞模式下調用read時沒有立刻讀到數據會當即返回-1,錯誤提示是(Resource temporarily unavailable)
並且會形成後面讀到的數據多是前一次要讀的數據,致使每一次都讀了前一次的數據。