Linux的網絡通信先后推出了select、poll、epoll三種模式。
select有以下三個問題:
(1)每次調用select,都需要把fd集合從用戶態拷貝到內核態,這個開銷在fd很多時會很大。
(2)同時每次調用select都需要在內核遍歷傳遞進來的所有fd,這個開銷在fd很多時也很大。
(3)select支持的文件描述符數量太小了,默認是1024。
poll解決了第三個問題,select保存描述符fd的數據結構是數組,poll改成了鏈表,突破了fd的個數限制。
但是第1和第2個問題依然存在。
epoll在poll的基礎上,又解決了前兩個問題:
(1)對第一個問題,epoll每次注冊新的事件到epoll句柄中時(在epoll_ctl中指定EPOLL_CTL_ADD),會把所有的fd拷貝進內核,而不是在epoll_wait的時候重復拷貝。這樣epoll保證了每個fd在整個過程中只會拷貝一次。
(2)對第二個問題,epoll單獨設置了一個就緒鏈表,當fd就緒(可讀/可寫)之后,放入就緒鏈表。epoll_wait只需要遍歷就緒鏈表,而不需要遍歷所有的fd,從而節省大量的CPU時間。
epoll有LT和ET兩種工作模式,默認工作模式是LT(水平觸發),高速工作模式是ET(邊緣觸發)。
LT是fd只要處於可讀或可寫狀態,就會通知用戶;ET只有不可讀變為可讀,或不可寫變為可寫之時,才會通知用戶。
ET對系統的調用,比LT要少得多,所以ET是高速工作模式,效率高很多。
用戶使用ET模式時,讀/寫fd的時候,必須連續讀/寫完(直到返回EAGAIN錯誤)。否則如果未讀/寫完,系統會認為狀態沒有變化,就不會再重復通知,這樣這個fd就死掉了。