Epoll爲什麼比select高效

1、select的原理
首先select在處理多客戶端接受數據的問題上是這樣處理的的,首先將要堅挺的socket添加到一個fs_set[]數組中,然後將在select時候將這些監聽的socket傳遞給內核(這些socket在內核中表示爲一個文件,這個文件對應有他的socket buffer用於存放接收發送數據),如果此時這些監聽的socket上有數據了,則直接返回.否則則會將這個進程後者線程阻塞(這裏阻塞的實現是將這個進程或者線程添加到這個socket(文件或者設備)的等待隊列中,也就是將這個進程從CPU的工作隊列添加等待隊列中),因爲是監聽了很多的socket,同時這些socket有沒有一個統一管理和維護的地方,因此每個socket都會維護這個socket對應的這個調用select的進程或者線程關係,每個socket都會將這個socket和這個線程關聯起來,並且把各自的進程或者線程告訴操作系統,將其掛起,也就是從CPU的工作隊列中移動到等待隊列中。這樣當前select的進程就不會被CPU調度到。如果此時網卡接受到數據,數據從網卡拷貝到內核中,這些數據中包含了端口號,所以知道這些數據來自那個socket.這樣就知道這個socket接受到數據了,此時就把這個socket對應的那個進程喚醒(此時其實就是select函數返回),但是這裏還有一步動作就是其他socket也把這個進程掛在他們的等待隊列中,所以這裏需要遍歷所有的 socket通知他們也把自己的對應的那個等待進程掛到運行隊列中去。此時用戶態select地方被返回。但是此時用戶態進程或者線程卻並不知道是那個socket上有數據,因此在select後還需要從fs_set[]數組中遍歷確定是那個socket有數據,然後在在這個socet上調用recv接口接收數據。接收完數據後,又重新調用select接口重新循環剛剛上面描述的動作。
這個過程存在一些問題:
1、每次調用select都需要拷貝fd_set從用戶態到內核態。
2、內核太監控到sockect返回,但是用戶態需要遍歷fd才能知道那些socket有數據,需要遍歷,效率很低。
3、內核態需要遍歷所有的socket摘除等待鏈表上的進程,遍歷代價太大。
4、正是因爲遍歷的原因,所以內核設置了最大監聽1024個socket的緣故,否則銷量太低了。
二、epoll解決select的問題
所以epoll就是來解決select的問題的,首先epoll不會每次把監聽的socket通過系統調用傳遞(拷貝)到內核態,而是在epoll_wait之前通過epoll_ctrl把需要監聽的 socket添加到epoll_fd上去管理和維護.這個epoll_fd是通過epoll_create創建的,這裏和select一個區別就是epoll多了一個epoll_fd來管理監聽的socket。這個epoll_fd也是傳遞給內核態,然後將用戶態需要監聽socket添加到這個epoll_fd的一個紅黑樹上,這裏使用紅黑樹的目的是需要查找,刪除,添加銷量高,這樣在調用epoll_wait期間都可以很高效的增刪監聽的socket.和select不一樣的是調用epoll_wait如果這個紅黑樹上的socket上有數據就返回,否則就把進程或者線程阻塞,這裏的阻塞只會將這個進程和這個epoll_fd幫頂起來,也就是把這個進程掛在這個epoll_fd文件(設備)的等待隊列上。同時這個epoll_fd還維護一個readylist(一個雙向鏈表),只要紅黑樹上的 socket有數據就將這個socket掛在read list上去,然後通知epoll_fd將對應的進程喚醒,添加到CPU運行隊列中。這樣用戶態的進程調用epoll_wait地方繼續執行,這裏還有一點需要說明的是,下面ready list上的socket會通過epoll_wait調用時候傳遞來一個epoll_event數組拷貝會用戶態,並且socket個數就是epoll_wait返回值返回的。這樣用戶態的進程既可以通過epoll_wait返回值和epoll_event[]數組中的socketfd就行數據的接收處理了。

三、總結
epoll對與select多了一個event_fd來管理所有的socket.而且一直內核態監聽中,但是select沒有這個東西,所以需要每次傳遞到內核態,這樣效率就很低小。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章