select()函數(shù)和poll()函數(shù)均是主要用來(lái)處理多路I/O復(fù)用的情況。比如一個(gè)服務(wù)器既想等待輸入終端到來(lái),又想等待若干個(gè)套接字有客戶請(qǐng)求到達(dá),這時(shí)候就需要借助select或者poll函數(shù)了。
(一)select()函數(shù)原型如下:
1 int select(int fdsp1, fd_set *readfds, fd_set *writefds, fd_set *errorfds, const struct timeval *timeout);
各個(gè)參數(shù)含義如下:
- int fdsp1:最大描述符值 + 1
- fd_set *readfds:對(duì)可讀感興趣的描述符集
- fd_set *writefds:對(duì)可寫(xiě)感興趣的描述符集
- fd_set *errorfds:對(duì)出錯(cuò)感興趣的描述符集
- struct timeval *timeout:超時(shí)時(shí)間(注意:對(duì)于linux系統(tǒng),此參數(shù)沒(méi)有const限制,每次select調(diào)用完畢timeout的值都被修改為剩余時(shí)間,而unix系統(tǒng)則不會(huì)改變timeout值)
select函數(shù)會(huì)在發(fā)生以下情況時(shí)返回:
- readfds集合中有描述符可讀
- writefds集合中有描述符可寫(xiě)
- errorfds集合中有描述符遇到錯(cuò)誤條件
- 指定的超時(shí)時(shí)間timeout到了
當(dāng)select返回時(shí),
描述符集合將被修改以指示哪些個(gè)描述符正處于可讀、可寫(xiě)或有錯(cuò)誤狀態(tài)。可以用FD_ISSET宏對(duì)描述符進(jìn)行測(cè)試以找到狀態(tài)變化的描述符。如果select因?yàn)槌瑫r(shí)而返回的話,所有的描述符集合都將被清空。
select函數(shù)返回狀態(tài)發(fā)生變化的描述符總數(shù)。返回0意味著超時(shí)。失敗則返回-1并設(shè)置errno。可能出現(xiàn)的錯(cuò)誤有:EBADF(無(wú)效描述符)、EINTR(因終端而返回)、EINVAL(nfds或timeout取值錯(cuò)誤)。
設(shè)置描述符集合通常用如下幾個(gè)宏定義:
1 FD_ZERO(fd_set *fdset); /* clear all bits in fdset */
2 FD_SET(int fd, fd_set *fdset); /* turn on the bit for fd in fd_set */
3 FD_CLR(int fd, fd_set *fdset); /* turn off the bit for fd in fd_set */
4 int FD_ISSET(int fd, fd_set *fdset); /* is the bit for fd on in fdset? */
如:
1 fd_set rset;
2 FD_ZERO(&rset); /* initialize the set: all bits off */
3 FD_SET(1, &rset); /* turn on bit for fd 1 */
4 FD_SET(4, &rset); /* turn on bit for fd 4 */
5 FD_SET(5, &rset); /* turn on bit for fd 5 */
當(dāng)select返回的時(shí)候,rset位都將被置0,除了那些有變化的fd位。
當(dāng)發(fā)生如下情況時(shí)認(rèn)為是可讀的:
- socket的receive buffer中的字節(jié)數(shù)大于socket的receive buffer的low-water mark屬性值。(low-water mark值類似于分水嶺,當(dāng)receive buffer中的字節(jié)數(shù)小于low-water mark值的時(shí)候,認(rèn)為socket還不可讀,只有當(dāng)receive buffer中的字節(jié)數(shù)達(dá)到一定量的時(shí)候才認(rèn)為socket可讀)
- 連接半關(guān)閉(讀關(guān)閉,即收到對(duì)端發(fā)來(lái)的FIN包)
- 發(fā)生變化的描述符是被動(dòng)套接字,而連接的三路握手完成的數(shù)量大于0,即有新的TCP連接建立
- 描述符發(fā)生錯(cuò)誤,如果調(diào)用read系統(tǒng)調(diào)用讀套接字的話會(huì)返回-1。
當(dāng)發(fā)生如下情況時(shí)認(rèn)為是可寫(xiě)的:
- socket的send buffer中的字節(jié)數(shù)大于socket的send buffer的low-water mark屬性值以及socket已經(jīng)連接或者不需要連接(如UDP)。
- 寫(xiě)半連接關(guān)閉,調(diào)用write函數(shù)將產(chǎn)生SIGPIPE
- 描述符發(fā)生錯(cuò)誤,如果調(diào)用write系統(tǒng)調(diào)用寫(xiě)套接字的話會(huì)返回-1。
注意:select默認(rèn)能處理的描述符數(shù)量是有上限的,為FD_SETSIZE的大小。對(duì)于timeout參數(shù),如果置為NULL,則表示wait forever;若timeout->tv_sec = timeout->tv_usec = 0,則表示do not wait at all;否則指定等待時(shí)間。如果使用select處理多個(gè)套接字,那么需要使用一個(gè)數(shù)組(也可以是其他結(jié)構(gòu))來(lái)記錄各個(gè)描述符的狀態(tài)。而使用poll則不需要,下面看poll函數(shù)。(二)poll()函數(shù)原型如下:
1 int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);
各參數(shù)含義如下:
- struct pollfd *fdarray:一個(gè)結(jié)構(gòu)體,用來(lái)保存各個(gè)描述符的相關(guān)狀態(tài)。
- unsigned long nfds:fdarray數(shù)組的大小,即里面包含有效成員的數(shù)量。
- int timeout:設(shè)定的超時(shí)時(shí)間。(以毫秒為單位)
poll函數(shù)返回值及含義如下:
- -1:有錯(cuò)誤產(chǎn)生
- 0:超時(shí)時(shí)間到,而且沒(méi)有描述符有狀態(tài)變化
- >0:有狀態(tài)變化的描述符個(gè)數(shù)
著重講fdarray數(shù)組,因?yàn)檫@是它和select()函數(shù)主要的不同的地方:
pollfd的結(jié)構(gòu)如下:
1 struct pollfd {
2 int fd; /* descriptor to check */
3 short events; /* events of interest on fd */
4 short revents; /* events that occured on fd */
5 };
其實(shí)poll()和select()函數(shù)要處理的問(wèn)題是相同的,只不過(guò)是不同組織在幾乎相同時(shí)刻同時(shí)推出的,因此才同時(shí)保留了下來(lái)。select()函數(shù)把可讀描述符、可寫(xiě)描述符、錯(cuò)誤描述符分在了三個(gè)集合里,這三個(gè)集合都是用bit位來(lái)標(biāo)記一個(gè)描述符,一旦有若干個(gè)描述符狀態(tài)發(fā)生變化,那么它將被置位,而其他沒(méi)有發(fā)生變化的描述符的bit位將被clear,也就是說(shuō)select()的readset、writeset、errorset是一個(gè)value-result類型,通過(guò)它們傳值,而也通過(guò)它們返回結(jié)果。這樣的一個(gè)壞處是每次重新select 的時(shí)候?qū)媳仨氈匦沦x值。而poll()函數(shù)則與select()采用的方式不同,它通過(guò)一個(gè)結(jié)構(gòu)數(shù)組保存各個(gè)描述符的狀態(tài),每個(gè)結(jié)構(gòu)體第一項(xiàng)fd代表描述符,第二項(xiàng)代表要監(jiān)聽(tīng)的事件,也就是感興趣的事件,而第三項(xiàng)代表poll()返回時(shí)描述符的返回狀態(tài)。合法狀態(tài)如下:
- POLLIN: 有普通數(shù)據(jù)或者優(yōu)先數(shù)據(jù)可讀
- POLLRDNORM: 有普通數(shù)據(jù)可讀
- POLLRDBAND: 有優(yōu)先數(shù)據(jù)可讀
- POLLPRI: 有緊急數(shù)據(jù)可讀
- POLLOUT: 有普通數(shù)據(jù)可寫(xiě)
- POLLWRNORM: 有普通數(shù)據(jù)可寫(xiě)
- POLLWRBAND: 有緊急數(shù)據(jù)可寫(xiě)
- POLLERR: 有錯(cuò)誤發(fā)生
- POLLHUP: 有描述符掛起事件發(fā)生
- POLLNVAL: 描述符非法
對(duì)于POLLIN | POLLPRI等價(jià)與select()的可讀事件;POLLOUT | POLLWRBAND等價(jià)與select()的可寫(xiě)事件;POLLIN 等價(jià)與POLLRDNORM | POLLRDBAND,而POLLOUT等價(jià)于POLLWRBAND。如果你對(duì)一個(gè)描述符的可讀事件和可寫(xiě)事件以及錯(cuò)誤等事件均感興趣那么你應(yīng)該都進(jìn)行相應(yīng)的設(shè)置。
對(duì)于timeout的設(shè)置如下:
- INFTIM: wait forever
- 0: return immediately, do not block
- >0: wait specified number of milliseconds
對(duì)于select()和poll()函數(shù)的講解暫時(shí)到此。 更多細(xì)節(jié)請(qǐng)參考下面這篇博文:
http://www.shnenglu.com/just51living/archive/2011/07/28/151995.html
posted on 2011-10-26 19:04
myjfm 閱讀(12007)
評(píng)論(0) 編輯 收藏 引用 所屬分類:
網(wǎng)絡(luò)編程