眾所周知,在Linux系統下所有設備都是以文件的形式存在,串口也一樣。通常I/O操作都是有阻塞與非阻塞的兩種方式。
其中"超時"這個概念其實是阻塞中的一種處理手段,本質還是屬於阻塞的I/O模式.
在Linux中串口的IO操作 本文將它分為三種狀態:
阻塞狀態
超時狀態
非阻塞狀態
這三種狀態的轉換組合有這麼幾種:
阻塞 --> 超時
阻塞 --> 非阻塞
超時 --> 阻塞
超時 --> 非阻塞
非阻塞 --> 阻塞
我們一個一個來分析
首先在一個串口的描述符打開的時候指定它的模式是阻塞還是阻塞
fd = open(
"/dev/tttyS0"
,O_RDWR | O_NOCTTY);
//以阻塞模式打開串口
fd = open(
"/dev/tttyS0"
,O_RDWR | O_NOCTTY | O_NDELAY);
//以非阻塞模式打開串口
//O_NDELAY 等價於 O_NOBLOCK
當一個串口是阻塞狀態的時候便可以設置它為超時狀態。
利用 struct termios 的 cc_t c_cc[NCCS] 成員
c_cc[VTIME] 非規范模式讀取時的超時時間(單位:百毫秒)
c_cc[VMIN] 非規范模式讀取時的最小字符數
如需需要設置超時則c_cc[VMIN] 必須等於0
這代表能夠讀取的最小字符是0個,即使用read讀取數據超時read返回0
有一個需要注意的地方!
當c_cc[VTIME] 設置為 0 且 c_cc[VMIN] == 0 的時候,代表超時0秒(姑且這麼叫吧!)
這個時候使用read讀取數據會立即返回(有讀到數據時返回字節數,沒有數據和一般超時一樣返回0)
但是!
雖然這時候在現象上看起來和非阻塞模式一樣(read都不會阻塞)但返回值不同
非阻塞模式: read沒有讀到數據立即返回-1
超時0秒時: read沒有讀到數據立即返回 0 (設置了超時的阻塞模式)
ret = read(fd,recvbuf,BUF_SIZE);
if
(ret == -1)
//非阻塞模式時"無數據返回"
{
//do something
}
ret = read(fd,recvbuf,BUF_SIZE);
if
(ret == 0)
//阻塞模式設置超時0秒時"超時返回"
{
//do something
}
雖然表現形式一樣,但在編程時必須要了解自己使用的是哪一種模式和串口當前的狀態才能更好的分析和處理問題。
這裡說一下我曾經遇到過的一個問題:
我在打開串口時使用阻塞模式打開,但是沒有設置c_cc[VMIN]的值,而它初始化後就是0,所以發現串口沒有被阻塞,其實原因就是串口模式還是阻塞模式沒錯,但是它是超時0秒的狀態,所以在沒有數據到達時read也返回了。
阻塞狀態和非阻塞狀態的切換
非阻塞狀態時使用
fcntl(fd,F_SETFL,0);
即可轉換成阻塞狀態,同樣可以設置超時
當非阻塞狀態已經設置了超時時,在轉換成阻塞狀態後超時也隨同生效
阻塞狀態時使用
fcntl(fd,F_SETFL,FNDELAY); //FNDELAY等價於FNONBLOCK
即可轉換成非阻塞狀態,超時失效
這裡提一下 fcntl.h中幾個宏的定義
/* Define some more compatibility macros to be backward compatible with
BSD systems which did not managed to hide these kernel macros. */
#ifdef __USE_BSD
# define FAPPEND O_APPEND
# define FFSYNC O_FSYNC
# define FASYNC O_ASYNC
# define FNONBLOCK O_NONBLOCK
# define FNDELAY O_NDELAY
#endif /* Use BSD. */
現在一目了然了吧?打開串口時非阻塞模式的O_NDELAY或O_NONBLOCK選項
fcntl設置非阻塞模式的第3個參數FNDELAY或者FNONBLOCK 其實都是O_NONBLOCK主要就是為了兼容