select: 輪詢+fd_set
1.采用fd_set存儲fd(fd_set通過數組位圖實現)
2.每次調用select,都需要把fd集合從用戶態拷貝到內核態,fd越多開銷越大
3.每次調用select,都需要在內核遍歷傳遞進來的fd,開銷大(輪詢)
4.select支持的fd數量太少,1024(32個32位的整數,也就是2的10次方),受fd_setsize參數限制,改變這個參數的話需要重新編譯內核
具體想要突破fd_setsize參數限制的話有以下2個方法:
1)修改sys/types.h頭文件中的fd_setsize值并且重新編譯內核,當然這并不是一個好的方法,fd_setsize的值變大意味著我們輪詢一次的時間需要加長
2)通過多核CPU,采用分而治之+負載均衡的思想將連接注冊在多個select上,并發select不僅能突破fd_setsize限制而且能提高性能,有點map -reduce的思想,這里還有一個問題,就是當一個輪詢器poller上的最大輪詢數量超過了1024還能不能注冊的問題,我們同樣可以采用分而治之的多線程輪詢策略來解決,比如連接數2000,線程1輪詢0到1023,線程2輪詢1024-2000
?
5.調用select,返回的是含有整個句柄的數組,需要遍歷整個數組才知道哪些句柄發生了事件(輪詢)
6.select的觸發是LT模式,效率不高
?
poll:輪詢+鏈表
1.采用鏈表存儲fd,沒有了fd數量限制,但是上述其他缺點依然存在
?
epoll:紅黑樹+雙鏈表+回調機制
1.紅黑樹掛載事件,事件發生時通過回調機制將事件添加到雙鏈表中
2.檢查是否有事件發生時,不需要輪詢,只需要檢查雙鏈表即可
3.保證每個fd只會被拷貝一次(事件被加入到epoll中時,fd就會被拷貝進入內核,而不是在epoll_wait時拷貝),使用了mmap加速內核與用戶空間的消息傳遞,無論是select還是poll都需要內核把fd消息通知給用戶空間,如何1避免不必要的內存拷貝就很重要,在這點上,epoll通過內核與用戶空間mmap同一塊內存實現!
4.fd數量上限為最大可以打開的文件數目,這個我們可以通過調整內核的參數來改變,通過ulimit -n來調整或者setrlimit函數設置,當然這個需要root權限,但是一個系統能打開的文件最大數目也是有限制的,取決于內存大小,可以通過cat/proc/sys/fs/file-max查看
該雙鏈表我們一般叫做就緒事件鏈表
?
當然,以上的優缺點僅僅是在高并發且任一時間只有少數socket活躍的特定場景下的,如果并發量高,socket都比較活躍的情況下,select就不見得會比epoll慢(就像我們常常是快排比插入排序快,但是在特定的情況下并不成立)
什么情況下使用select,poll的性能好于使用epoll?
高并發,少活躍socket的情況下,select的輪詢在于每次輪詢都會輪詢到不活躍的套接字,從而浪費了時間導致效率慢
但是當高并發,多活躍socket的情況下,select的輪詢每次都能很快的輪詢到活躍的套接字,這種情況下也是可以考慮使用select的!
?
最后,總結一下
并發低 ? socket活躍 ? select/poll
并發低 ? socket不活躍 select/poll
這兩種情況使用epoll[紅黑樹+鏈表+回調機制]有點殺雞用牛刀的感覺
?
并發高 ? socket活躍 ? ? ? select/poll的性能不一定比epoll差
并發高 ? socket不活躍 ? ? epoll
?
?
?
//先碼著,后面準備自己實現一下這三種機制
?
?