轉載地址:http://blog.endlesscode.com/2010/03/27/select-poll-epoll-intro/by Stephen posted
on 2010/03/27
對select、poll、epoll瞭解得不多,下面是從《構建高效能Web網站》摘錄下來的介紹,等以後真正接觸到select、poll和epoll方面的開發再詳細寫一下使用上的區別。
select
select最早於1983年出現在4.2BSD中,它通過一個select()系統調用來監視多個檔案描述符的數組,當select()返回後,該數組中就緒的檔案描述符便會被核心修改標誌位,使得進程可以獲得這些檔案描述符從而進行後續的讀寫操作。
select目前幾乎在所有的平台上支援,其良好跨平台支援也是它的一個優點,事實上從現在看來,這也是它所剩不多的優點之一。
select的一個缺點在於單個進程能夠監視的檔案描述符的數量存在最大限制,在Linux上一般為1024,不過可以通過修改宏定義甚至重新編譯核心的方式提升這一限制。
另外,select()所維護的儲存大量檔案描述符的資料結構,隨著檔案描述符數量的增大,其複製的開銷也線性增長。同時,由於網路回應時間的延遲使得大量TCP串連處於非活躍狀態,但調用select()會對所有socket進行一次線性掃描,所以這也浪費了一定的開銷。
poll
poll在1986年誕生於System V Release 3,它和select在本質上沒有多大差別,但是poll沒有最大檔案描述符數量的限制。
poll和select同樣存在一個缺點就是,包含大量檔案描述符的數組被整體複製於使用者態和核心的地址空間之間,而不論這些檔案描述符是否就緒,它的開銷隨著檔案描述符數量的增加而線性增大。
另外,select()和poll()將就緒的檔案描述符告訴進程後,如果進程沒有對其進行IO操作,那麼下次調用select()和poll()的時候將再次報告這些檔案描述符,所以它們一般不會丟失就緒的訊息,這種方式稱為水平觸發(Level Triggered)。
epoll
直到Linux2.6才出現了由核心直接支援的實現方法,那就是epoll,它幾乎具備了之前所說的一切優點,被公認為Linux2.6下效能最好的多路I/O就緒通知方法。
epoll可以同時支援水平觸發和邊緣觸發(Edge Triggered,只告訴進程哪些檔案描述符剛剛變為就緒狀態,它只說一遍,如果我們沒有採取行動,那麼它將不會再次告知,這種方式稱為邊緣觸發),理論上邊緣觸發的效能要更高一些,但是代碼實現相當複雜。
epoll同樣只告知那些就緒的檔案描述符,而且當我們調用epoll_wait()獲得就緒檔案描述符時,返回的不是實際的描述符,而是一個代表就緒描述符數量的值,你只需要去epoll指定的一個數組中依次取得相應數量的檔案描述符即可,這裡也使用了記憶體映射(mmap)技術,這樣便徹底省掉了這些檔案描述符在系統調用時複製的開銷。
另一個本質的改進在於epoll採用基於事件的就緒通知方式。在select/poll中,進程只有在調用一定的方法後,核心才對所有監視的檔案描述符進行掃描,而epoll事先通過epoll_ctl()來註冊一個檔案描述符,一旦基於某個檔案描述符就緒時,核心會採用類似callback的回調機制,迅速啟用這個檔案描述符,當進程調用epoll_wait()時便得到通知。