兩種IO模型,都屬於多路IO就緒通知,提供了對大量文件描述符就緒檢查的高性能方案,只不過實現方式有所不一樣:linux
select:apache
一個select()系統調用來監視包含多個文件描述符的數組,當select返回,該數組中就緒的文件描述符便會被內核修改標誌位。數組
select的 跨平臺 作的很好,幾乎每一個平臺都支持。 服務器
select缺點有如下三點:網絡
poll:數據結構
poll是unix沿用select本身從新實現了一遍,惟一解決的問題是poll 沒有最大文件描述符數量的限制多線程
epoll:架構
epoll帶來了兩個優點,大幅度提高了性能:併發
固然epoll也有必定的侷限性, epoll只有Linux2.6纔有實現 ,而其餘平臺都沒有,這和apache這種優秀的跨平臺服務器,顯然是有些背道而馳了。socket
select的本質是採用32個整數的32位,即32*32= 1024來標識,fd值爲1-1024。當fd的值超過1024限制時,就必須修改FD_SETSIZE的大小。這個時候就能夠標識32*max值範圍的fd。
對於單進程多線程,每一個線程處理多個fd的狀況,select是不適合的。
1.全部的線程均是從1-32*max進行掃描,每一個線程處理的均是一段fd值,這樣作有點浪費
2.1024上限問題,一個處理多個用戶的進程,fd值遠遠大於1024
因此這個時候應該採用poll,
poll傳遞的是數組頭指針和該數組的長度,只要數組的長度不是很長,性能仍是很不錯的,由於poll一次在內核中申請4K(一個頁的大小來存放fd),儘可能控制在4K之內
epoll仍是poll的一種優化,返回後不須要對全部的fd進行遍歷,在內核中維持了fd的列表。select和poll是將這個內核列表維持在用戶態,而後傳遞到內核中。可是隻有在2.6的內核才支持。
epoll更適合於處理大量的fd ,且活躍fd不是不少的狀況,畢竟fd較多仍是一個串行的操做
=====================================================================
對select、poll、epoll瞭解得很少,下面是從《構建高性能Web站點》摘錄下來的介紹,等之後真正接觸到select、poll和epoll方面的開發再詳細寫一下使用上的區別。
select最先於1983年出如今4.2BSD中,它經過一個select()系統調用來監視多個文件描述符的數組,當select()返回後,該數組中就緒的文件描述符便會被內核修改標誌位,使得進程能夠得到這些文件描述符從而進行後續的讀寫操做。
select目前幾乎在全部的平臺上支持,其良好跨平臺支持也是它的一個優勢,事實上從如今看來,這也是它所剩很少的優勢之一。
select的一個缺點在於單個進程可以監視的文件描述符的數量存在最大限制,在Linux上通常爲1024,不過能夠經過修改宏定義甚至從新編譯內核的方式提高這一限制。
另外,select()所維護的存儲大量文件描述符的數據結構,隨着文件描述符數量的增大,其複製的開銷也線性增加。同時,因爲網絡響應時間的延遲使得大量TCP鏈接處於非活躍狀態,但調用select()會對全部socket進行一次線性掃描,因此這也浪費了必定的開銷。
poll在1986年誕生於System V Release 3,它和select在本質上沒有多大差異,可是poll沒有最大文件描述符數量的限制。
poll和select一樣存在一個缺點就是,包含大量文件描述符的數組被總體複製於用戶態和內核的地址空間之間,而不論這些文件描述符是否就緒,它的開銷隨着文件描述符數量的增長而線性增大。
另外,select()和poll()將就緒的文件描述符告訴進程後,若是進程沒有對其進行IO操做,那麼下次調用select()和poll()的時候將再次報告這些文件描述符,因此它們通常不會丟失就緒的消息,這種方式稱爲水平觸發(Level Triggered)。
直到Linux2.6纔出現了由內核直接支持的實現方法,那就是epoll,它幾乎具有了以前所說的一切優勢,被公認爲Linux2.6下性能最好的多路I/O就緒通知方法。
epoll能夠同時支持水平觸發和邊緣觸發(Edge Triggered,只告訴進程哪些文件描述符剛剛變爲就緒狀態,它只說一遍,若是咱們沒有采起行動,那麼它將不會再次告知,這種方式稱爲邊緣觸發),理論上邊緣觸發的性能要更高一些,可是代碼實現至關複雜。
epoll一樣只告知那些就緒的文件描述符,並且當咱們調用epoll_wait()得到就緒文件描述符時,返回的不是實際的描述符,而是一個表明就緒描述符數量的值,你只須要去epoll指定的一個數組中依次取得相應數量的文件描述符便可,這裏也使用了內存映射(mmap)技術,這樣便完全省掉了這些文件描述符在系統調用時複製的開銷。
另外一個本質的改進在於epoll採用基於事件的就緒通知方式。在select/poll中,進程只有在調用必定的方法後,內核纔對全部監視的文件描述符進行掃描,而epoll事先經過epoll_ctl()來註冊一個文件描述符,一旦基於某個文件描述符就緒時,內核會採用相似callback的回調機制,迅速激活這個文件描述符,當進程調用epoll_wait()時便獲得通知。
epoll的優勢:
1.支持一個進程打開大數目的socket描述符(FD)
select 最不能忍受的是一個進程所打開的FD是有必定限制的,由FD_SETSIZE設置,默認值是2048。對於那些須要支持的上萬鏈接數目的IM服務器來講顯然太少了。這時候你一是能夠選擇修改這個宏而後從新編譯內核,不過資料也同時指出這樣會帶來網絡效率的降低,二是能夠選擇多進程的解決方案(傳統的 Apache方案),不過雖然linux上面建立進程的代價比較小,但仍舊是不可忽視的,加上進程間數據同步遠比不上線程間同步的高效,因此也不是一種完美的方案。不過 epoll則沒有這個限制,它所支持的FD上限是最大能夠打開文件的數目,這個數字通常遠大於2048,舉個例子,在1GB內存的機器上大約是10萬左右,具體數目能夠cat /proc/sys/fs/file-max察看,通常來講這個數目和系統內存關係很大。
2.IO效率不隨FD數目增長而線性降低
傳統的select/poll另外一個致命弱點就是當你擁有一個很大的socket集合,不過因爲網絡延時,任一時間只有部分的socket是"活躍"的,可是select/poll每次調用都會線性掃描所有的集合,致使效率呈現線性降低。可是epoll不存在這個問題,它只會對"活躍"的socket進行操做---這是由於在內核實現中epoll是根據每一個fd上面的callback函數實現的。那麼,只有"活躍"的socket纔會主動的去調用 callback函數,其餘idle狀態socket則不會,在這點上,epoll實現了一個"僞"AIO,由於這時候推進力在os內核。在一些 benchmark中,若是全部的socket基本上都是活躍的---好比一個高速LAN環境,epoll並不比select/poll有什麼效率,相反,若是過多使用epoll_ctl,效率相比還有稍微的降低。可是一旦使用idle connections模擬WAN環境,epoll的效率就遠在select/poll之上了。
3.使用mmap加速內核與用戶空間的消息傳遞。
這點實際上涉及到epoll的具體實現了。不管是select,poll仍是epoll都須要內核把FD消息通知給用戶空間,如何避免沒必要要的內存拷貝就很重要,在這點上,epoll是經過內核於用戶空間mmap同一塊內存實現的。而若是你想我同樣從2.5內核就關注epoll的話,必定不會忘記手工 mmap這一步的。
4.內核微調
這一點其實不算epoll的優勢了,而是整個linux平臺的優勢。也許你能夠懷疑linux平臺,可是你沒法迴避linux平臺賦予你微調內核的能力。好比,內核TCP/IP協議棧使用內存池管理sk_buff結構,那麼能夠在運行時期動態調整這個內存pool(skb_head_pool)的大小--- 經過echo XXXX>/proc/sys/net/core/hot_list_length完成。再好比listen函數的第2個參數(TCP完成3次握手的數據包隊列長度),也能夠根據你平臺內存大小動態調整。更甚至在一個數據包面數目巨大但同時每一個數據包自己大小卻很小的特殊系統上嘗試最新的NAPI網卡驅動架構。
Apache與Nginx的性能誰更高效,取決於其服務器的併發策略以及其面對的場景:
併發策略:
咱們目前使用的 Apache是基於一個線程處理一個請求的非阻塞IO併發策略 。這種方式容許一個進程中經過多個線程來處理多個鏈接,其中每一個線程處理一個鏈接。Apache使用其worker模塊實現這種方式,目的是減小perfork模式中太多進程的開銷,使得apache能夠支持更多的併發鏈接。
至於,非阻塞IO的實現,是經過一個子進程負責accept(),一旦接收到鏈接後,便將任務分配給適當worker的線程。
因爲apache的線程使用的是內核進程調度器管理的輕量級進程,所以與perfork模式比較,進程上下文切換的開銷依然存在,性能提高不是很明顯。
而 Nginx使用的是一個進程處理多個鏈接、非阻塞IO模式 ,這種模式最特別的是設計了獨立的listener進程,專門負責接收新的鏈接,再分配給各個worker,固然爲了減小任務調度的開銷,通常都是由worker進程來進行接收。
而IO模型層面,Nginx選擇epoll,此方式高效主要在於其基於事件的就緒通知機制,在高鏈接數的場景下,epoll通知方式更具優點。另外,epoll方式只關注活躍鏈接,而不像select方式須要掃描全部的文件描述符,這樣在大量鏈接的場景下,epoll方式優點會更加明顯。