select poll 与epoll模型的总结
select()和poll() IO多路復用模型
select優點:
1.一次可以等待多個文件描述符,減少了平均等待時間
2.客戶越來越多時,減輕了進程調度的壓力(相較于多進程多線程服務器)
select缺點:
1.能監聽的文件描述符有上限,這個上限是由fd_set決定的。
2.它返回的只是就緒事件的個數,要判斷是那個事件滿足,需要遍歷文件描述符。
3.select監聽的集合是輸入輸出參數,每次監聽都需要重新初始化。
4.每次調用select,都需要把fd集合從用戶態拷貝到內核態,這個開銷在fd很多時會很大
5.內核采用輪詢(遍歷fd集合)的方式來檢測就緒事件,這個開銷在fd很多時也很大
6.select和poll都只能工作在低效的LT(水平觸發)模式
poll優點:
1.poll監聽的文件描述符沒有最大數量的限制
2.poll對于select來說包含了一個pollfd結構,pollfd結構包含了要監視的event和發生的revent,而不像select那樣使用輸入輸出的傳遞方式。所以不需要每次監聽都初始化
poll缺點:
1.數量過大以后其效率也會線性下降。
2.poll和select一樣也是返回就緒事件的個數,需要遍歷文件描述符來判斷是那個事件已經就緒,當數量很大時,開銷也就很大。
3.select和poll都只能工作在低效的LT(水平觸發)模式
4.每次調用poll,都需要把pollfd數組從用戶態拷貝到內核態,這個開銷在fd很多時會很大
5.內核采用輪詢(遍歷pollfd數組)的方式來檢測就緒事件,這個開銷在fd很多時也很大
拿select模型為例,假設我們的服務器需要支持100萬的并發連接,則在__FD_SETSIZE 為1024的情況下,則我們至少需要開辟1k個進程才能實現100萬的并發連接。除了進程間上下文切換的時間消耗外,從內核/用戶空間大量的無腦內存拷貝、數組輪詢等,是系統難以承受的。因此,基于select模型的服務器程序,要達到10萬級別的并發訪問,是一個很難完成的任務。
因此,該epoll上場了。
epoll IO多路復用模型實現機制
由于epoll的實現機制與select/poll機制完全不同,上面所說的?select的缺點在epoll上不復存在。
設想一下如下場景:有100萬個客戶端同時與一個服務器進程保持著TCP連接。而每一時刻,通常只有幾百上千個TCP連接是活躍的(事實上大部分場景都是這種情況)。如何實現這樣的高并發?
在select/poll時代,服務器進程每次都把這100萬個連接告訴操作系統(從用戶態復制句柄數據結構到內核態),讓操作系統內核去查詢這些套接字上是否有事件發生,輪詢完后,再將句柄數據復制到用戶態,讓服務器應用程序輪詢處理已發生的網絡事件,這一過程資源消耗較大,因此,select/poll一般只能處理幾千的并發連接。
epoll的設計和實現與select完全不同。epoll通過在Linux內核中申請一個簡易的文件系統(文件系統一般用什么數據結構實現?紅黑樹)。把原先的select/poll調用分成了3個部分:
1)調用epoll_create()建立一個epoll對象(在epoll文件系統中為這個句柄對象分配資源)
2)調用epoll_ctl向epoll對象中添加這100萬個連接的套接字
3)調用epoll_wait收集發生的事件的連接
如此一來,要實現上面說是的場景,只需要在進程啟動時建立一個epoll對象,然后在需要的時候向這個epoll對象中添加或者刪除連接。同時,epoll_wait的效率也非常高,因為調用epoll_wait時,并沒有一股腦的向操作系統復制這100萬個連接的句柄數據,內核也不需要去遍歷全部的連接。
下面來看看linux內核具體的epoll機制實現思路。
當某一進程調用epoll_create方法時,Linux內核會創建一個eventpoll結構體,這個結構體中有兩個成員與epoll的使用方式密切相關。eventpoll結構體如下所示:
[cpp]?view plaincopy每一個epoll對象都有一個獨立的eventpoll結構體,用于存放通過epoll_ctl方法向epoll對象中添加進來的事件。這些事件都會掛載在紅黑樹中,如此,重復添加的事件就可以通過紅黑樹而高效的識別出來(紅黑樹的插入時間效率是lgn,其中n為樹的高度)。
而所有添加到epoll中的事件都會與設備(網卡)驅動程序建立回調關系,也就是說,當相應的事件發生時會調用這個回調方法。這個回調方法在內核中叫ep_poll_callback,它會將發生的事件添加到rdlist雙鏈表中。
在epoll中,對于每一個事件,都會建立一個epitem結構體,如下所示:
[cpp]?view plaincopy當調用epoll_wait檢查是否有事件發生時,只需要檢查eventpoll對象中的rdlist雙鏈表中是否有epitem元素即可。如果rdlist不為空,則把發生的事件復制到用戶態,同時將事件數量返回給用戶。
epoll數據結構示意圖
從上面的講解可知:通過紅黑樹和雙鏈表數據結構,并結合回調機制,造就了epoll的高效。
OK,講解完了Epoll的機理,我們便能很容易掌握epoll的用法了。一句話描述就是:三步曲。
第一步:epoll_create()系統調用。此調用返回一個句柄,之后所有的使用都依靠這個句柄來標識。
第二步:epoll_ctl()系統調用。通過此調用向epoll對象中添加、刪除、修改感興趣的事件,返回0標識成功,返回-1表示失敗。
第三部:epoll_wait()系統調用。通過此調用收集收集在epoll監控中已經發生的事件。
epoll優點:
1.epoll維護的描述符數目不受到限制,而且性能不會隨著描述符數目的增加而下降。(不需要遍歷整個文件描述符)
2.epoll先通過epoll_ctl注冊一個描述符到內核中,并一直維護著而不像poll每次操作都將所有要監控的描述符傳遞給內核
3.在描述符讀寫就緒時,通過回掉函數將自己加入就緒隊列中,之后epoll_wait返回該就緒隊列,所以用戶不需要遍歷整個文件描述符判斷哪些事件就緒。性能提升。
4.支持ET高效模式。
poll和select適用于關心描述符個數多的應用程序。其中epoll對于每次只有很少描述符就緒很有優勢(采用回調機制監測描述符就緒)。
總結
以上是生活随笔為你收集整理的select poll 与epoll模型的总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c++实现单例类(懒汉与饿汉)
- 下一篇: 经典的大数据例题