select() 和poll()的用法

 

select() 和poll()方法是複用網絡套接字的強大工具。採用這兩個方法可以表明過程在什麼時候可以安全地執行打開的文件描述符而沒有任何延遲。比方說,程序員就可以用這些函數調用得知某個套接字上何時有數據被讀取。在給select()和poll()指定任務之後你就不必經常性地檢查套接字是否有數據要讀取了。實際上,select()和 poll()還可以置於操作系統的後臺運行,一旦滿足特定事件或者時間超時就會被喚醒。這個進程可以顯著地增加程序的效率(假如你更關心程序的性能而非可以移植性,我們在本文的末尾簡單討論了兩種替代select()和 poll()的方法)。


就象你看到的那樣,select()和 poll()在功能上非常相似。在很多情況下,select()和poll()的方法實現其實是互相映射的。比方說,在Apache 2.0的核心組件Apache可移植運行時內,可移植接口(portable interface)就是模仿poll()語法提供的。在沒有本地poll()實現的平臺上, poll()的語法就被映射爲select()。在FreeBSD系統上,select()的libc_r實現就完全是對poll()系統調用的簡單封裝。
select() 說明

Single UNI規範第2版(SUSv2)是這樣定義select() 的:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, structtimeval *timeout);

該函數調用有以下參數:

* int nfds – 所有給定集合內最高文件描述符加1
* fd_set *readfds –在有數據讀取時觸發函數返回的文件描述符
* fd_set *writefds – 在有數據準備寫入時觸發函數返回的文件描述符
* fd_set *errorfds – 發生錯誤反常時觸發函數返回的文件描述符
* structtimeval *timeout –select()必須等待事件的最大週期

返回值表示其請求事件滿足條件的文件描述符的數目。

你不能通過直接改變fd_set結構值這種方式來修改fd_set結構。設置或者獲值的唯一可移植方式是採用FD_*宏:

* FD_ZERO(fd_set *) –把fd_set初始化爲空
* FD_CLR(intfd, fd_set *) – 從fd_set中刪除關聯的fd
* FD_SET(intfd, fd_set *) – 在fd_set中增加關聯的fd
* FD_ISSET(intfd, fd_set *) – 假如fd在fd_set內即可返回非0值


在從select()返回時,FD_ISSET()可以針對給定集合內的每個fd而被調用來表明其條件是否滿足。

timeout值的含義是這樣的,你可以指定select()等待事件的時間需要多久。假如timeout的值是NULL,那麼select()將無限期地等待事件。假如超時的timeval結構設置爲0,那麼select()將立即返回而不會等待任何事件的發生。否則,timeout則具體定義 select()函數等待的時間長度。SUSv2規定所有遵守規範的函數實現都應該支持至少31天的超時。不妨查閱 清單A 瞭解select()函數的具體應用示例。
poll()說明

poll()方法試圖合併select()函數的參數,同時提供範圍更廣的事件通知。SUSv2 如下定義poll() 函數:
int poll(struct pollfd fds[ ], nfds_t nfds, int timeout);

參數含義如下:

* struct pollfd fds[ ] - pollfd結構數組
* nfds_t nfds - fds[ ]中文件描述符集合的數目
* int timeout - poll()等待事件發生的時間長度(單位是毫秒)


返回值表示多少fds有事件發生。

pollfd結構通常包括以下結構成員:

* intfd – 表示某個事件由哪個fd監視
* short events – 表示哪些事件將被監視的位字段
* short revents – 表示調用poll()時檢測到的事件的比特位

SUSv2規範對以上內容和事件位字段的含義進行了詳細的說明。同select()函數相比,poll()在確定被處理的事件類型方面具有更大一些的靈活性。除了讀、寫和錯誤通知以外,poll()函數還支持帶外和高優先級數據的直接識別。

不同於select(),poll()的timeout參數是一個簡單的整數,表示poll()等待事件的時長。可以賦以特殊的值,通常是 -1 或者常數INFTIM,這是很多舊系統常用的參數,指示poll()永遠等待事件。同select()函數一樣,0 超時表示 poll()函數必須立即返回。

在清單B中,清單A中的select()示例就用poll()代替了。
可替代select()和poll()的方法

即便通過select()或者 poll()函數複用事件通知具有突出的優點,不過,其他具有類似功能的函數實現也可以達到同樣的性能。然而,這些實現在跨平臺方面沒有實現標準化。你必須在使用這些特定函數實現同喪失可移植性之間進行權衡。我們現在就討論一下兩個替代方法:Solaris系統下的/dev/poll和FreeBSD系統下的kqueue。


如你看到的那樣,以上兩種實現通過以下辦法提高了關鍵的性能,實際上,在現實的應用環境中,開發人員幾乎總是針對 select()或者 poll()函數連續地採用同一fds。在每次採用同樣參數的時候爲了消除這一重複性所帶來的麻煩,本來需要檢查的fds的就被緩衝了。這一措施在監視大量fds的時候效率特別高,因爲某些select()和poll()實現存在可伸縮性方面的問題。
Solaris系統下的/dev/poll

在Solaris 7系統上,Sun引入了/dev/poll設備。在使用 /dev/poll的時候,你首先要打開/dev/poll作爲一個普通文件。然後構造pollfd結構,方式同普通的poll()函數調用一樣。這些 pollfd結構隨後寫入到打開的 /dev/poll 文件描述符。在打開句柄的生存週期內, /dev/poll會根據pollfd結構返回事件(注意,pollfd結構內的事件字段中的特定POLLREMOVE將從/dev/poll的列表中刪除對應的fd)。通過調用特定的ioctl (DP_POLL) 和dvpoll,程序就可以從/dev/poll獲得需要的信息。在使用dvpoll結構的情況下,發生的事件就可以被檢測到了。
Linux支持和其他資源

增加/dev/poll對Linux 2.4系列的支持是有相應補丁程序的,但是,目前這些補丁程序還沒有被主流Linux內核接受。在FreeBSD網站上,你可以查閱Solaris、Red Hat以及其他操作系統的有關手冊信息瞭解這方面的情況。
FreeBSD系統下的kqueue

在FreeBSD 4.1中推出。FreeBSD的kqueue API設計爲比其他對應函數提供更爲廣泛的事件通知能力。kqueue API提供了一套通用過濾器,可以模仿poll()語法(EVFILT_READ和EVFILT_WRITE)。不過,它還實現了文件系統變化(EVFILT_VNODE)、進程狀態變更(EVFILT_PROC)和信號交付(EVFILT_SIGNAL)的有關通知。要了解有關kqueue的詳細情況可以從BSDCon 2000下載Jonathan Lemon的論文(PDF格式)“Kqueue: A generic and scalable event notification facility”

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章