多路IO复用模型 select epoll 等
同步阻塞IO在等待數據就緒上花去太多時間,而傳統的同步非阻塞IO雖然不會阻塞進程,但是結合輪詢來判斷數據是否就緒仍然會耗費大量的CPU時間。
多路IO復用提供了對大量文件描述符進行就緒檢查的高性能方案。
?
select
select誕生于4.2BSD,在幾乎所有平臺上都支持,其良好的跨平臺支持是它的主要的也是為數不多的優點之一。
select的缺點(1)單個進程能夠監視的文件描述符的數量存在最大限制(2)select需要復制大量的句柄數據結構,產生巨大的開銷 (3)select返回的是含有整個句柄的列表,應用程序需要遍歷整個列表才能發現哪些句柄發生了事件(4)select的觸發方式是水平觸發,應用程序如果沒有完成對一個已經就緒的文件描述符進行IO操作,那么之后每次select調用還是會將這些文件描述符通知進程。相對應方式的是邊緣觸發。
?
poll
poll 誕生于UNIX System V Release 3,那時AT&T已經停止了UNIX的源代碼授權,所以顯然也不會直接使用BSD的select,所以AT&T自己實現了一個和select沒有多大差別的poll。
poll和select是名字不同的孿生兄弟,除了沒有監視文件數量的限制,select后面3條缺點同樣適用于poll。
面對select和poll的缺陷,不同的OS做出了不同的解決方案,可謂百花齊放。不過他們至少完成了下面兩點超越,一是內核長期維護一個事件關注列表,我們只需要修改這個列表,而不需要將句柄數據結構復制到內核中;二是直接返回事件列表,而不是所有句柄列表。
/dev/poll
Sun在Solaris中提出了新的實現方案,它使用了虛擬的/dev/poll設備,開發者可以將要監視的文件描述符加入這個設備,然后通過ioctl()來等待事件通知。
/dev/epoll
名為/dev/epoll的設備以補丁的方式出現在Linux2.4中,它提供了類似/dev/poll的功能,并且在一定程度上使用mmap提高了性能。
?
kqueue
FreeBSD實現了kqueue,可以支持水平觸發和邊緣觸發,性能和下面要提到的epoll非常接近。
?
epoll
epoll誕生于Linux 2.6內核,被公認為是Linux2.6下性能最好的多路IO復用方法。
| int epoll_create(int size)? |
| ? |
| int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)? |
| ? |
| int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout) |
- epoll_create 創建 kernel 中的關注事件表,相當于創建 fd_set
- epoll_ctl 修改這個表,相當于 FD_SET 等操作?
- epoll_wait等待 I/O事件發生,相當于 select/poll 函數?
epoll支持水平觸發和邊緣觸發,理論上來說邊緣觸發性能更高,但是使用更加復雜,因為任何意外的丟失事件都會造成請求處理錯誤。Nginx就使用了epoll的邊緣觸發模型。
這里提一下水平觸發和邊緣觸發就緒通知的區別,這兩個詞來源于計算機硬件設計。它們的區別是只要句柄滿足某種狀態,水平觸發就會發出通知;而只有當句柄狀態改變時,邊緣觸發才會發出通知。例如一個socket經過長時間等待后接收到一段100k的數據,兩種觸發方式都會向程序發出就緒通知。假設程序從這個socket中讀取了50k數據,并再次調用監聽函數,水平觸發依然會發出就緒通知,而邊緣觸發會因為socket“有數據可讀”這個狀態沒有發生變化而不發出通知且陷入長時間的等待。
因此在使用邊緣觸發的 api 時,要注意每次都要讀到 socket返回 EWOULDBLOCK為止
?
=================================================================================
?
http://bbs.linuxpk.com/thread-43628-1-1.html我們先來介紹下nginx??nginx :
支持高并發連接.官方測試的是5w并發連接但在實際生產中可制成2-4w并發連接數,得益于nginx使用最新的epoll(linux 2.6內核)和kqueue(freebsd)網絡I/O模型.而apache使用的則是傳統的select模型,其比較穩定的prefork模式為多進程模式,需要經常派生子進程,所消耗的CPU等服務器資源要比nginx高的多.
select 和epoll效率差的原因: select是輪詢、epoll是觸發式的,所以效率高。單單這樣講,那能懂了才見鬼了.好...我們暫且客觀的記住這句話.
先說Select:?
1.Socket數量限制:該模式可操作的Socket數由FD_SETSIZE決定,內核默認32*32=1024.?
2.操作限制:通過遍歷FD_SETSIZE(1024)個Socket來完成調度,不管哪個Socket是活躍的,都遍歷一遍.?
后說Poll:?
1.Socket數量幾乎無限制:該模式下的Socket對應的fd列表由一個數組來保存,大小不限(默認4k).?
2.操作限制:同Select.?
再說:Epoll:?
1.Socket數量無限制:同Poll?
2.操作無限制:基于內核提供的反射模式,有活躍Socket時,內核訪問該Socket的callback,不需要遍歷輪詢. 但是當所有Socket都活躍的時候,這時候所有的callback都被喚醒,會導致資源的競爭.既然都是要處理所有的Socket,那么遍歷是最簡單最有效的實現方式.
舉例來說:?
對于IM服務器,服務器和服務器之間都是長鏈接,但數量不多,一般一臺60\70個,比如采用ICE這種架構設計,但請求相當頻繁和密集,這時候通過反射喚醒callback不一定比用select去遍歷處理更好.?
對于web portal(門戶)服務器,都是瀏覽器客戶端發起的http短鏈接請求,數量很大,好一點的網站動輒每分鐘上千個請求過來,同時服務器端還有更多的閑置等待超時的Socket,這時候沒必要把全部的Socket都遍歷處理,因為那些等待超時的請求是大多數的,這樣用Epoll會更好.
支持一個進程打開大數目的socket描述符
select 最不能忍受的是一個進程所打開的FD是有一定限制的,由FD_SETSIZE設置,默認值是1024。對于那些需要支持的上萬連接數目的IM服務器來說顯然太少了。這時候你一是可以選擇修改這個宏然后重新編譯內核,不過資料也同時指出這樣會帶來網絡效率的下降,二是可以選擇多進程的解決方案(傳統的 Apache方案),不過雖然linux上面創建進程的代價比較小,但仍舊是不可忽視的,加上進程間數據同步遠 比不上線程間同步的高效,所以也不是一種完美的方案。不過 epoll則沒有這個限制,它所支持的FD上限是最大可以打開文件的數目,這個數字一般遠大于2048,舉個例子,在1GB內存的機器上大約是10萬左右,具體數目可以cat /proc/sys/fs/file-max察看,一般來說這個數目和系統內存關系很大。
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之上了。?
使用mmap加速內核與用戶空間的消息傳遞。?
這點實際上涉及到epoll的具體實現了。無論是select,poll還是epoll都需要內核把FD消息通知給用戶空間,如何避免不必要的內存拷貝就很重要,在這點上,epoll是通過內核于用戶空間mmap同一塊內存實現的。而如果你想我一樣從2.5內核就關注epoll的話,一定不會忘記手工 mmap這一步的。
內核微調
這一點其實不算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網 卡驅動架構。
select模式低效的原因
select 模式低效是由select的定義所決定的,與操作系統實現無關,任何內核在實現select時必須做輪循,才能知道這些socket的情況,這是會消耗 cpu的。此外,當你擁有一個很大socket集的時候,盡管任一時間只有小部分的socket是"活躍"的,但每次你都不得不將所有的socket填入到一個FD_SET中,這也會消耗一些cpu,并且當select返回后,處理業務時你可能還需要做“上下文映射”,同樣也會有一些性能影響,因此 select比epoll相對低效。
epoll的適用情景就是大量的socket,但是活躍多不是很高的情況。? ?
還有 kqueue,實際上有不少服務器是基于 BSD 開發的
kqueue 和 epoll 類似,據說效率上稍微高一些,不過沒比較過?
總結
以上是生活随笔為你收集整理的多路IO复用模型 select epoll 等的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 魔法引用函数magic_quotes_g
- 下一篇: Linux下各类TCP网络服务器的实现源