五種I/O 模式——阻塞(預設IO模式),非阻塞(常用語管道),I/O多工(IO多工應用情境),訊號I/O,非同步I/O
Linux下的I/O操作預設是阻塞I/O,即open和socket建立的I/O都是阻塞I/O
非阻塞 I/O (可以通過fcntl或者open時使用O_NONBLOCK參數,將fd設定為非阻塞的I/O)
//非阻塞模式,相當於告訴了系統核心: “當我請求的I/O 操作不能夠馬上完成,請馬上返回一個錯誤給我。”#include <sys/time.h>#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <assert.h>#include <errno.h>int main (){ int keyboard; int ret,i; char c; keyboard = open("/dev/tty",O_RDONLY | O_NONBLOCK); //非阻塞 //keyboard = open("/dev/tty",O_RDONLY); assert(keyboard>0); while(1) { if((i = read(keyboard,&c,1)) != -1){ if('\n'==c){ continue; } printf("Input is %c\n",c); sleep(3); if ('q'==c){ break; } }else if(i == -1){ perror("緩衝區沒有資料可讀\n"); sleep(3); }else{ printf("%s\n", "Timeout 逾時"); sleep(3); } }}
運行結果: # (如果使用者沒有輸入資料)
緩衝區沒有資料可讀
: Resource temporarily unavailable
緩衝區沒有資料可讀
: Resource temporarily unavailable
1 #輸入資料
Input is 1
q
Input is q
//預設是阻塞狀態 //keyboard = open("/dev/tty",O_RDONLY | O_NONBLOCK); //非阻塞 keyboard = open("/dev/tty",O_RDONLY);
運行結果:# (如果使用者沒有輸入資料) read()不會返回,阻塞在那裡了。
1
#輸入資料
Input is 1
q
Input is q
阻塞方式block,就是進程或是線程執行到這些函數時必須等待某個事件的發生,如果事件沒有發生,進程或線程就被阻塞,函數不能立即返回。使用Select就可以完成非阻塞non-block,就是進程或線程執行此函數時不必非要等待事件的發生,一旦執行肯定返回,以傳回值的不同來反映函數的執行情況,如果事件發生則與阻塞方式相同,若事件沒有發生則返回一個代碼來告知事件未發生,而進程或線程繼續執行,所以效率較高。select能夠監視我們需要監視的檔案描述符的變化情況。
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
1、ndfs:select中監視的檔案控制代碼數,一般設為要監視的檔案中的最大檔案號加一。
2、rdfds:select()監視的可讀檔案控制代碼集合,當rdfds映象的檔案控制代碼狀態變成可讀時系統告訴select函數返回。
這個集合中有一個檔案可讀,select就會返回一個大於0的值,表示有檔案可讀,
如果沒有可讀的檔案,則根據timeout參數再判斷是否逾時,
若超出timeout的時間,select返回0,若發生錯誤返回負值,
可以傳入NULL值,表示不關心任何檔案的讀變化;
3、wtfds: select()監視的可寫檔案控制代碼集合
4、exfds:select()監視的異常檔案控制代碼集合,當exfds映象的檔案控制代碼上有特殊情況發生時系統會告訴select函數返回。
5、timeout:select()的逾時結束時間。
這個參數它使select處於三種狀態,
第一,若將NULL以形參傳入,即不傳入時間結構,就是將select置於阻塞狀態,
一定等到監視檔案描述符集合中某個檔案描述符發生變化為止;
第二,若將時間值設為0秒0毫秒,就變成一個純粹的非阻塞函數,不管檔案描述符是否有變化,
都立刻返回繼續執行,檔案無變化返回0,有變化返回一個正值;
第三,timeout的值大於0,這就是等待的逾時時間,即select在timeout時間內阻塞,
逾時時間之內有事件到來就返回了,否則在逾時後不管怎樣一定返回,傳回值同上述
6、RETURN VALUE
On success, select() and pselect() return the number of file descriptors contained in the
three returned descriptor sets (that is, the total number of bits that are set in readfds,
writefds, exceptfds) which may be zero if the timeout expires before anything interesting
happens. On error, -1 is returned, and errno is set appropriately; the sets and timeout
become undefined, so do not rely on their contents after an error.
void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);樣本:
#include <sys/time.h>#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <assert.h>int main (){ int keyboard; int ret,i; char c; fd_set readfd; struct timeval timeout; keyboard = open("/dev/tty",O_RDONLY | O_NONBLOCK); //非阻塞 assert(keyboard>0); while(1) { timeout.tv_sec=2; timeout.tv_usec=0; FD_ZERO(&readfd); FD_SET(keyboard,&readfd); ret=select(keyboard+1,&readfd,NULL,NULL,&timeout); if(FD_ISSET(keyboard,&readfd)) { i=read(keyboard,&c,1); if('\n'==c){ continue; } printf("Input is %c\n",c); if ('q'==c){ break; } }else{ printf("%s\n", "Timeout 逾時"); } }}
運行結果:
Timeout 逾時
q
Input is q