其它文檔:
http://www.cnitblog.com/zouzheng/archive/2010/11/25/71711.html
(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;
}