在I/O編程過程當中,當須要同時處理多個客戶端接入請求時,能夠利用多線程或者I/O多路複用技術進行處理。I/O多路複用技術經過把多個I/O的阻塞複用到同一個select的阻塞上,從而使得系統在單線程的狀況下能夠同時處理多個客戶端請求。與傳統的多線程/多進程模型比,I/O多路複用的最大優點是系統開銷小,系統不須要建立新的額外進程或者線程,也不須要維護這些進程和線程的運行,降底了系統的維護工做量,節省了系統資源,I/O多路複用的主要應用場景以下:編程
服務器須要同時處理多個處於監聽狀態或者多個鏈接狀態的套接字。服務器
服務器須要同時處理多種網絡協議的套接字。網絡
目前支持I/O多路複用的系統調用有 select,pselect,poll,epoll,在Linux網絡編程過程當中,很長一段時間都使用select作輪詢和網絡事件通知,然而select的一些固有缺陷致使了它的應用受到了很大的限制,最終Linux不得不在新的內核版本中尋找select的替代方案,最終選擇了epoll。epoll與select的原理比較相似,爲了克服select的缺點,epoll做了不少重大改進,現總結以下:多線程
1. 支持一個進程打開的socket描述符(FD)不受限制(僅受限於操做系統的最大文件句柄數)。socket
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加速內核與用戶空間的消息傳遞spa
不管是select,poll仍是epoll都須要內核把FD消息通知給用戶空間,如何避免沒必要要的內存複製就顯得很是重要,epoll是經過內核和用戶空間mmap使用同一塊內存實現。操作系統
4. epoll的API更加簡單
用來克服select/poll缺點的方法不僅有epoll,epoll只是一種Linux的實現方案。在freeBSD下有kqueue,而dev/poll是最古老的Solaris的方案,使用難度依次遞增。但epoll更加簡單。