Linux串口IO模式的一些心得
眾所周知,在Linux系統(tǒng)下所有設(shè)備都是以文件的形式存在,串口也一樣。
通常I/O操作都是有阻塞與非阻塞的兩種方式。
其中"超時"這個概念其實是阻塞中的一種處理手段,本質(zhì)還是屬于阻塞的I/O模式.
在Linux中串口的IO操作 本文將它分為三種狀態(tài):
- 阻塞狀態(tài)
- 超時狀態(tài)
- 非阻塞狀態(tài)
這三種狀態(tài)的轉(zhuǎn)換組合有這么幾種:
- 阻塞 --> 超時
- 阻塞 --> 非阻塞
- 超時 --> 阻塞
- 超時 --> 非阻塞
- 非阻塞 --> 阻塞
我們一個一個來分析
首先在一個串口的描述符打開的時候指定它的模式是阻塞還是阻塞
- fd = open("/dev/tttyS0",O_RDWR | O_NOCTTY);//以阻塞模式打開串口
- fd = open("/dev/tttyS0",O_RDWR | O_NOCTTY | O_NDELAY);//以非阻塞模式打開串口
- //O_NDELAY 等價于 O_NOBLOCK
當(dāng)一個串口是阻塞狀態(tài)的時候便可以設(shè)置它為超時狀態(tài)。
利用 struct termios 的 cc_t c_cc[NCCS] 成員
- c_cc[VTIME] 非規(guī)范模式讀取時的超時時間(單位:百毫秒)
- c_cc[VMIN] 非規(guī)范模式讀取時的最小字符數(shù)
如需需要設(shè)置超時則c_cc[VMIN] 必須等于0。這代表能夠讀取的最小字符是0個,即使用read讀取數(shù)據(jù)超時read返回0
有一個需要注意的地方!
當(dāng)c_cc[VTIME] 設(shè)置為 0 且 c_cc[VMIN] == 0 的時候,代表超時0秒(姑且這么叫吧!)這個時候使用read讀取數(shù)據(jù)會立即返回(有讀到數(shù)據(jù)時返回字節(jié)數(shù),沒有數(shù)據(jù)和一般超時一樣返回0)但是,雖然這時候在現(xiàn)象上看起來和非阻塞模式一樣(read都不會阻塞)但返回值不同
非阻塞模式: read沒有讀到數(shù)據(jù)立即返回-1
超時0秒時: read沒有讀到數(shù)據(jù)立即返回 0 (設(shè)置了超時的阻塞模式)
- ret = read(fd,recvbuf,BUF_SIZE);
- if(ret == -1)//非阻塞模式時"無數(shù)據(jù)返回"
- {
- //do something
- }
- ret = read(fd,recvbuf,BUF_SIZE);
- if(ret == 0)//阻塞模式設(shè)置超時0秒時"超時返回"
- {
- //do something
- }
雖然表現(xiàn)形式一樣,但在編程時必須要了解自己使用的是哪一種模式和串口當(dāng)前的狀態(tài)才能更好的分析和處理問題。
這里說一下我曾經(jīng)遇到過的一個問題:
我在打開串口時使用阻塞模式打開,但是沒有設(shè)置c_cc[VMIN]的值,而它初始化后就是0,所以發(fā)現(xiàn)串口沒有被阻塞,其實原因就是串口模式還是阻塞模式?jīng)]錯,但是它是超時0秒的狀態(tài),所以在沒有數(shù)據(jù)到達(dá)時read也返回了。
阻塞狀態(tài)和非阻塞狀態(tài)的切換
非阻塞狀態(tài)時使用
- fcntl(fd,F_SETFL,0);
即可轉(zhuǎn)換成阻塞狀態(tài),同樣可以設(shè)置超時
當(dāng)非阻塞狀態(tài)已經(jīng)設(shè)置了超時時,在轉(zhuǎn)換成阻塞狀態(tài)后超時也隨同生效
阻塞狀態(tài)時使用
- fcntl(fd,F_SETFL,FNDELAY);
- //FNDELAY等價于FNONBLOCK
即可轉(zhuǎn)換成非阻塞狀態(tài),超時失效
這里提一下 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. */
現(xiàn)在一目了然了吧?打開串口時非阻塞模式的O_NDELAY或O_NONBLOCK選項
fcntl設(shè)置非阻塞模式的第3個參數(shù)FNDELAY或者FNONBLOCK 其實都是O_NONBLOCK主要就是為了兼容