select目前幾乎在全部的平臺上支持,
其良好跨平臺支持也是它的一個優勢
。select的一個缺點在於單個進程可以監視的文件描述符的數量存在最大限制
,在Linux上通常爲1024,
能夠經過修改宏定義甚至從新編譯內核的方式提高這一限制
,可是這樣也會形成效率的下降。
select本質上是經過設置或者檢查存放fd標誌位的數據結構來進行下一步處理
。這樣所帶來的缺點是:
一、select最大的缺陷就是單個進程所打開的FD是有必定限制的,它由FD_SETSIZE設置,默認值是1024。
通常來講這個數目和系統內存關係很大,具體數目能夠cat /proc/sys/fs/file-max察看
。32位機默認是1024個。64位機默認是2048.
二、對socket進行掃描時是線性掃描,即採用輪詢的方法,效率較低。
當套接字比較多的時候,每次select()都要經過遍歷FD_SETSIZE個Socket來完成調度,無論哪一個Socket是活躍的,都遍歷一遍。這會浪費不少CPU時間。若是能給套接字註冊某個回調函數,當他們活躍時,自動完成相關操做,那就避免了輪詢
,這正是epoll與kqueue作的。
三、須要維護一個用來存放大量fd的數據結構,這樣會使得用戶空間和內核空間在傳遞該結構時複製開銷大。
二、poll
基本原理:
poll本質上和select沒有區別,它將用戶傳入的數組拷貝到內核空間
,而後查詢每一個fd對應的設備狀態,若是設備就緒則在設備等待隊列中加入一項並繼續遍歷,若是遍歷完全部fd後沒有發現就緒設備,則掛起當前進程,直到設備就緒或者主動超時,被喚醒後它又要再次遍歷fd。這個過程經歷了屢次無謂的遍歷。
它沒有最大鏈接數的限制,緣由是它是基於鏈表來存儲的
,可是一樣有一個缺點:
1)大量的fd的數組被總體複製於用戶態和內核地址空間之間
,而無論這樣的複製是否是有意義。
2)poll還有一個特色是「水平觸發」
,若是報告了fd後,沒有被處理,那麼下次poll時會再次報告該fd。
注意:從上面看,select和poll都須要在返回後,
經過遍歷文件描述符來獲取已經就緒的socket
。事實上,
同時鏈接的大量客戶端在一時刻可能只有不多的處於就緒狀態
,所以隨着監視的描述符數量的增加,其效率也會線性降低。
三、epoll
epoll是在2.6內核中提出的,是以前的select和poll的加強版本。相對於select和poll來講,epoll更加靈活,沒有描述符限制。
epoll使用一個文件描述符管理多個描述符,將用戶關係的文件描述符的事件存放到內核的一個事件表中,這樣在用戶空間和內核空間的copy只需一次
。
基本原理:
epoll支持水平觸發和邊緣觸發,最大的特色在於邊緣觸發,它只告訴進程哪些fd剛剛變爲就緒態,而且只會通知一次
。還有一個特色是,
epoll使用「事件」的就緒通知方式
,經過epoll_ctl註冊fd,
一旦該fd就緒,內核就會採用相似callback的回調機制來激活該fd
,epoll_wait即可以收到通知。
epoll的優勢:
一、沒有最大併發鏈接的限制
,能打開的FD的上限遠大於1024(1G的內存上能監聽約10萬個端口)。
二、效率提高,不是輪詢的方式,不會隨着FD數目的增長效率降低
。
只有活躍可用的FD纔會調用callback函數;
即Epoll最大的優勢就在於它只管你「活躍」的鏈接,而跟鏈接總數無關
,所以在實際的網絡環境中,Epoll的效率就會遠遠高於select和poll。
三、內存拷貝
,利用mmap()文件映射內存加速與內核空間的消息傳遞;即epoll使用mmap減小複製開銷
。
epoll對文件描述符的操做有兩種模式:
LT(level trigger)和ET(edge trigger)
。LT模式是默認模式,LT模式與ET模式的區別以下:
LT模式:當epoll_wait檢測到描述符事件發生並將此事件通知應用程序,
應用程序能夠不當即處理該事件
。下次調用epoll_wait時,會再次響應應用程序並通知此事件。
ET模式:當epoll_wait檢測到描述符事件發生並將此事件通知應用程序,
應用程序必須當即處理該事件
。若是不處理,下次調用epoll_wait時,不會再次響應應用程序並通知此事件。
一、LT模式
LT(level triggered)是缺省的工做方式,而且同時支持block和no-block socket
。在這種作法中,內核告訴你一個文件描述符是否就緒了,而後你能夠對這個就緒的fd進行IO操做。
若是你不做任何操做,內核仍是會繼續通知你的
。
二、ET模式
ET(edge-triggered)是高速工做方式,只支持no-block socket
。在這種模式下,當描述符從未就緒變爲就緒時,內核經過epoll告訴你。而後它會假設你知道文件描述符已經就緒,而且不會再爲那個文件描述符發送更多的就緒通知,直到你作了某些操做致使那個文件描述符再也不爲就緒狀態了(好比,你在發送,接收或者接收請求,或者發送接收的數據少於必定量時致使了一個EWOULDBLOCK 錯誤)。
可是請注意,若是一直不對這個fd做IO操做(從而致使它再次變成未就緒),內核不會發送更多的通知(only once)
。
ET模式在很大程度上減小了epoll事件被重複觸發的次數,所以效率要比LT模式高
。epoll工做在ET模式的時候,
必須使用非阻塞套接口
,以免因爲一個文件句柄的阻塞讀/阻塞寫操做把處理多個文件描述符的任務餓死。
三、在select/poll中,
進程只有在調用必定的方法後,內核纔對全部監視的文件描述符進行掃描
,而epoll事先經過epoll_ctl()來註冊一個文件描述符,
一旦基於某個文件描述符就緒時,內核會採用相似callback的回調機制
,迅速激活這個文件描述符,當進程調用epoll_wait()時便獲得通知。(
此處去掉了遍歷文件描述符,而是經過監聽回調的的機制。這正是epoll的魅力所在。
)
注意:若是沒有大量的idle-connection或者dead-connection,epoll的效率並不會比select/poll高不少,可是當遇到大量的idle-connection,就會發現epoll的效率大大高於select/poll。
3、select、poll、epoll區別
一、支持一個進程所能打開的最大鏈接數