1. 程式人生 > >java併發程式設計之IO基礎入門之I/O多路複用技術

java併發程式設計之IO基礎入門之I/O多路複用技術

在I/O程式設計過程中,當需要同時處理多個客戶端接入請求時,可以利用多執行緒或者I/O多路複用技術進行處理。I/O多路複用技術通過把多個I/O的阻塞複用到同一個select的阻塞上,從而使得系統在單執行緒的情況下可以同時處理多個客戶端請求。與傳統的多執行緒/多程序模型比,I/O多路複用的最大優勢是系統開銷小,系統不需要建立新的額外程序或者執行緒,也不需要維護這些程序和執行緒的執行,降底了系統的維護工作量,節省了系統資源,I/O多路複用的主要應用場景如下:

  • 伺服器需要同時處理多個處於監聽狀態或者多個連線狀態的套接字

  • 伺服器需要同時處理多種網路協議的套接字

目前支援I/O多路複用的系統呼叫有 select

pselectpollepoll,在Linux網路程式設計過程中,很長一段時間都使用select做輪詢和網路事件通知,然而select的一些固有缺陷導致了它的應用受到了很大的限制,最終Linux不得不在新的核心版本中尋找select的替代方案,最終選擇了epoll。epoll與select的原理比較類似,為了克服select的缺點,epoll作了很多重大改進,現總結如下:

1. 支援一個程序開啟的socket描述符(FD)不受限制(僅受限於作業系統的最大檔案控制代碼數)

select最大的缺陷就是單個程序所開啟的FD是有一定限制的,它由FD_SETSIZE設定,預設值是1024。對於那些需要支援上萬個TCP連線的大型伺服器來說顯然太少了。可以選擇修改這個巨集,然後重新編譯核心,不過這會帶來網路效率的下降。我們也可以通過選擇多程序的方案(傳統的Apache方案)解決這個問題,不過雖然在Linux上建立程序的代價比較小,但仍舊是不可忽視的,另外,程序間的資料交換非常麻煩,對於Java由於沒有共享記憶體,需要通過Socket通訊或者其他方式進行資料同步,這帶來了額外的效能損耗,增加了程式複雜度,所以也不是一種完美的解決方案。值得慶幸的是,epoll並沒有這個限制,它所支援的FD上限是作業系統的最大檔案控制代碼數

,這個數字遠遠大於1024。例如,在1GB記憶體的機器上大約是10萬個控制代碼左右,具體的值可以通過cat/proc/sys/fs/filemax察看,通常情況下這個值跟系統的記憶體關係比較大。

2. I/O效率不會隨著FD數目的增加而線性下降

傳統的select/poll另一個致命弱點就是當你擁有一個很大的socket集合,由於網路延時或者鏈路空閒,任一時刻只有少部分的socket是“活躍”的,但是select/poll每次呼叫都會線性掃描全部集合,導致效率呈現線性下降。epoll不存在這個問題,它只會對“活躍”的socket進行操作-這是因為在核心實現中epoll是根據每個fd上面的callback函式實現的,那麼,只有“活躍”的socket才會主動的去呼叫callback函式,其他idle狀態socket則不會

。在這點上,epoll實現了一個偽AIO。針對epoll和select效能對比的benchmark測試表明:如果所有的socket都處於活躍態。例如一個高速LAN環境,epoll並不比select/poll效率高太多;相反,如果過多使用epoll_ctl,效率相比還有稍微的下降。但是一旦使用idle connections模擬WAN環境,epoll的效率就遠在select/poll之上了。

3. 使用mmap加速核心與使用者空間的訊息傳遞

無論是select,poll還是epoll都需要核心把FD訊息通知給使用者空間,如何避免不必要的記憶體複製就顯得非常重要,epoll是通過核心和使用者空間mmap使用同一塊記憶體實現。

4. epoll的API更加簡單

用來克服select/poll缺點的方法不只有epoll,epoll只是一種Linux的實現方案。在freeBSD下有kqueue,而dev/poll是最古老的Solaris的方案,使用難度依次遞增。但epoll更加簡單。