再談select, iocp, epoll,kqueue及各種I/O複用機制
上篇文章太過幼稚,一派胡言,希望沒有誤導大家。。我也是菜鳥,學習本身就是一個不斷追求真理的過程,希望能諒解:)首先感謝kasicass GG指出錯誤(已經指導我很多次了,非常感謝~~),然後我又查閱了一些資料,再次整理,more seriously and detailedly~
首先,介紹幾種常見的I/O模型及其區別,如下:
-
blocking I/O
-
nonblocking I/O
-
I/O multiplexing (select and poll)
-
signal driven I/O (SIGIO)
-
asynchronous I/O (the POSIX aio_
blocking I/O
這個不用多解釋吧,阻塞套接字。下圖是它呼叫過程的圖示:
重點解釋下上圖,下面例子都會講到。首先application呼叫 recvfrom()轉入kernel,注意kernel有2個過程,wait for data和copy data from kernel to user。直到最後copy complete後,recvfrom()才返回。此過程一直是阻塞的。
nonblocking I/O:
與blocking I/O對立的,非阻塞套接字,呼叫過程圖如下:
可以看見,如果直接操作它,那就是個輪詢。。直到核心緩衝區有資料。
I/O multiplexing (select and poll)
最常見的I/O複用模型,select。
select先阻塞,有活動套接字才返回。與blocking I/O相比,select會有兩次系統呼叫,但是select能處理多個套接字。
signal driven I/O (SIGIO)
只有UNIX系統支援,感興趣的課查閱相關資料
與I/O multiplexing (select and poll)相比,它的優勢是,免去了select的阻塞與輪詢,當有活躍套接字時,由註冊的handler處理。
asynchronous I/O (the POSIX aio_functions)
很少有*nix系統支援,windows的IOCP則是此模型
完全非同步的I/O複用機制,因為縱觀上面其它四種模型,至少都會在由kernel copy data to appliction時阻塞。而該模型是當copy完成後才通知application,可見是純非同步的。好像只有windows的完成埠是這個模型,效率也很出色。
下面是以上五種模型的比較
可以看出,越往後,阻塞越少,理論上效率也是最優。
=====================分割線==================================
5種模型的比較比較清晰了,剩下的就是把select,epoll,iocp,kqueue按號入座那就OK了。
select和iocp分別對應第3種與第5種模型,那麼epoll與kqueue呢?其實也於select屬於同一種模型,只是更高階一些,可以看作有了第4種模型的某些特性,如callback機制。
那麼,為什麼epoll,kqueue比select高階?
答案是,他們無輪詢。因為他們用callback取代了。想想看,當套接字比較多的時候,每次select()都要通過遍歷FD_SETSIZE個Socket來完成排程,不管哪個Socket是活躍的,都遍歷一遍。這會浪費很多CPU時間。如果能給套接字註冊某個回撥函式,當他們活躍時,自動完成相關操作,那就避免了輪詢,這正是epoll與kqueue做的。
windows or *nix (IOCP or kqueue/epoll)?
誠然,Windows的IOCP非常出色,目前很少有支援asynchronous I/O的系統,但是由於其系統本身的侷限性,大型伺服器還是在UNIX下。而且正如上面所述,kqueue/epoll 與 IOCP相比,就是多了一層從核心copy資料到應用層的阻塞,從而不能算作asynchronous I/O類。但是,這層小小的阻塞無足輕重,kqueue與epoll已經做得很優秀了。
提供一致的介面,IO Design Patterns
實際上,不管是哪種模型,都可以抽象一層出來,提供一致的介面,廣為人知的有ACE,Libevent這些,他們都是跨平臺的,而且他們自動選擇最優的I/O複用機制,使用者只需呼叫介面即可。說到這裡又得說說2個設計模式,Reactor and Proactor。有一篇經典文章http://www.artima.com/articles/io_design_patterns.html值得閱讀,Libevent是Reactor模型,ACE提供Proactor模型。實際都是對各種I/O複用機制的封裝。
Java nio包是什麼I/O機制?
我曾天真的認為java nio封裝的是IOCP。。現在可以確定,目前的java本質是select()模型,可以檢查/jre/bin/nio.dll得知。至於java伺服器為什麼效率還不錯。。我也不得而知,可能是設計得比較好吧。。-_-。
=====================分割線==================================
總結一些重點:
- 只有IOCP是asynchronous I/O,其他機制或多或少都會有一點阻塞。
- select低效是因為每次它都需要輪詢。但低效也是相對的,視情況而定,也可通過良好的設計改善
- epoll, kqueue是Reacor模式,IOCP是Proactor模式。
- java nio包是select模型。。
OVER,寫得很累,轉載請註明出處,謝謝!