從I O復用談epoll為什麼高效

2022-05-17 15:15:28 字數 1540 閱讀 6074

很多人提到網路就說epoll,認為epoll效率是最高的。單純的這麼認為,其實有失偏頗。epoll固然高效,可是它是怎麼做到高效的,它到底比select或poll優異在哪兒?

我們通過呼叫流程來簡單分析下。

首先以select為例(poll類似),看下其呼叫過程

1.選擇想要處理的套接字,通過介面fd_set(fd, &set)加入到set中;

2.呼叫select(max+1, &set,,..)

3.對set中所有套接字呼叫fd_isset(fd,&set),檢視fd上是否有事件發生

select存在的問題

單個程序能夠監視的檔案描述符的數量存在最大限制,通常是1024,當然可以更改數量,但由於select採用輪詢的方式掃瞄檔案描述符,檔案描述符數量越多,效能越差;(在linux核心標頭檔案中,有這樣的定義:#define __fd_setsize    1024)

核心 / 使用者空間記憶體拷貝問題,select需要複製大量的控制代碼資料結構,產生巨大的開銷;

select返回的是含有整個控制代碼的陣列,應用程式需要遍歷整個陣列才能發現哪些控制代碼發生了事件;

select的觸發方式是水平觸發,應用程式如果沒有完成對乙個已經就緒的檔案描述符進行io操作,那麼之後每次select呼叫還是會將這些檔案描述符通知程序。

epoll呼叫過程

1 .epoll_create 建立乙個epoll物件,一般epollfd = epoll_create()

2 .epoll_ctl (epoll_add/epoll_del的合體),往epoll物件中增加/刪除某乙個流的某乙個事件

比如epoll_ctl(epollfd, epoll_ctl_add, socket, epollin);//註冊緩衝區非空事件,即有資料流入     

epoll_ctl(epollfd, epoll_ctl_del, socket, epollout);//註冊緩衝區非滿事件,即流可以被寫入

新增事件的時候,其實是向核心註冊了乙個**函式。**函式作用是,在相應的套接字上發生事件時,將其加入到epoll物件的時間就緒鍊錶中,而這是在核心完成的。

3 epoll_wait(epollfd,...),獲取就緒事件。即從就緒事件鍊錶中取出所有的事件。

可以看到epoll比select高效的地方在於,其返回的就是所有已經發生事件的套接字,而不需要像select那樣需要在使用者態去判斷每個套接字上是否有事件發生。

另外,在呼叫select時,核心需要去一一檢測傳入的套接字集合是否有事件,而呼叫epoll_wait時,只是將核心中的就緒資料取出而已

如果有n個連線,並且這n個連線都有事件發生,那麼使用select與epoll其實並沒有多少區別。對於select來說,使用者態對每乙個套接字的事件監測都是有效的。

但是select有乙個問題是,每次去呼叫select之前,都要重置套接字set。如果連線數很大,每次fd_set(fd, &set)呼叫介面,也會對效能造成不小的影響。而epoll中,只需呼叫一次epoll_ctl即可。

所以,在連線數很大,且活躍連線不多的情況下,使用epoll有明顯的優勢;而如果連線數較少,且連線基本都是活躍的,其實select的效果反而會更好。

IO多路復用的引出(為什麼要用IO多路復用)

程序數量有限 代價太高 銷毀,上下文切換 受限於cpu 記憶體隔離 程序間通訊代價高 以上都是不可避免的的 受限於cpu 那麼影響的是響應能力 例如1000個執行緒都在發收 全部排程,響應能力肯定受影響 阻塞那麼會有同學說改為非阻塞不就行了嗎?那麼改為非阻塞不能完成收發資訊就會結束,那麼迴圈檢測 e...

為什麼IO復用還需要搭配非阻塞IO

當資料達到socket緩衝區時,可能會因為一些原因被核心丟棄,比如,校驗和錯誤,這時io復用喚醒執行緒對socket讀並不能讀到資料,如果是阻塞io就會被阻塞住。這個達到緩衝區的資料不一定被丟棄,但是也有可能被別人取走了,比如多個程序accept同乙個套接字時引發的驚群現象,只有乙個連線到來,但是所...

從ucOS談,為什麼需要作業系統?

1.作業系統可以顯著降低開發難度。作業系統幫我們協調多個程式之間的耦合關係,比如我們需要將串列埠的接收到的資料顯示在一塊lcd上。傳統做法是如圖1所示 圖1.無作業系統流程 該流程中當串列埠接收到資料再呼叫lcd顯示程式將資料顯示出來,而圖2展示了一種基於作業系統的方法。在該方法中串列埠接收資料和l...