(1)阻塞block
所謂阻塞方式block,顧名思義,就是進程或是線程執行到這些函數時必須等待某個事件的發生,如果事件沒有發生,進程或線程就被阻塞,函數不能立即返回。
例如socket編程中connect、accept、recv、recvfrom這樣的阻塞程式。
再如絕大多數的函數調用、語句執行,嚴格來說,他們都是以阻塞方式執行的。
(2)非阻塞non-block
所謂非阻塞方式non-block,就是進程或線程執行此函數時不必非要等待事件的發生,一旦執行肯定返回,以傳回值的不同來反映函數的執行情況,如果事件發生則與阻塞方式相同,若事件沒有發生則返回一個代碼來告知事件未發生,而進程或線程繼續執行,所以效率較高。
比如程式語句:int len=read(fd,buffer,BUFSIZE);函數read唯讀一次,不管讀到資料或是沒有讀到資料,它都返回結果。又如while(1){len=read(fd,buffer,BUFSIZE);if(…)break;},雖然可以迴圈讀取想要的資料,但它是非阻塞的,會大大地浪費系統資源。
備忘:在socket編程中使用:fcntl(sockfd,F_SETFL,O_NONBLOCK);會把sockfd設定為非阻塞模式,則之後的connect、accept、recv、recvfrom等函數便失去了阻塞功能,變成了非阻塞函數。
(3)select函數
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
上面的非阻塞式的while迴圈顯然是不可取的,而失去阻塞功能的connect等函數也需要改進,對於這兩種情況,select函數便可以大顯身手了。
關於select函數在這方面的使用,已經有兩篇文章講得十分清楚了:
http://blog.chinaunix.net/u/11557/showart_104967.html
http://blog.ednchina.com/thinkker/151601/message.aspx
關於select函數的使用,有幾點需要注意的地方:
maxfdp 為所有fd中的最大值加1.
readfds 和 timeout 在每次執行select前都要重新初始化. 對於readfds,每次迴圈都要清空集合,否則不能檢測描述符變化;而對於timeout,每次都要初始化其值,否則timeout被預設初始化為0.
//正確使用select函數的典型樣本(程式段):int Read(int fd, char *readbuf, int BUFSIZE){ int len1,len2,nfds,select_ret; struct timeval timeout; fd_set readfds; FD_ZERO(&readfds); FD_SET(fd1, &readfds); FD_SET(fd2, &readfds); nfds=fd1>fd2?(fd1+1):(fd2+1); timeout.tv_sec = 1; timeout.tv_usec = 500000; while ((select_ret = select(nfds, &readfds, NULL, NULL, &timeout)) > 0) { len1 += read(fd1, readbuf1 + len, BUFSIZE1 - len); len2 += read(fd2, readbuf2 + len, BUFSIZE2 - len); FD_ZERO(&readfds); FD_SET(fd1, &readfds); FD_SET(fd2, &readfds); nfds=fd1>fd2?(fd1+1):(fd2+1); timeout.tv_sec = 0; timeout.tv_usec = 500000; } readbuf1[BUFSIZE1-1]='\0'; readbuf2[BUFSIZE2-1]='\0'; return len1+len2;}