epoll原理

轉載地址:https://www.cnblogs.com/pluser/p/epoll_principles.html

epoll原理

設想一個場景:有100萬用戶同時與一個進程保持着TCP連接,而每一時刻只有幾十個或幾百個TCP連接是活躍的(接收TCP包),也就是說在每一時刻進程只需要處理這100萬連接中的一小部分連接。那麼,如何才能高效的處理這種場景呢?進程是否在每次詢問操作系統收集有事件發生的TCP連接時,把這100萬個連接告訴操作系統,然後由操作系統找出其中有事件發生的幾百個連接呢?實際上,在Linux2.4版本以前,那時的select或者poll事件驅動方式是這樣做的。

這裏有個非常明顯的問題,即在某一時刻,進程收集有事件的連接時,其實這100萬連接中的大部分都是沒有事件發生的。因此如果每次收集事件時,都把100萬連接的套接字傳給操作系統(這首先是用戶態內存到內核態內存的大量複製),而由操作系統內核尋找這些連接上有沒有未處理的事件,將會是巨大的資源浪費,然後select和poll就是這樣做的,因此它們最多隻能處理幾千個併發連接。而epoll不這樣做,它在Linux內核中申請了一個簡易的文件系統,把原先的一個select或poll調用分成了3部分:

1)調用epoll_create建立一個epoll對象(在epoll文件系統中給這個句柄分配資源);

2)調用epoll_ctl向epoll對象中添加這100萬個連接的套接字;

3)調用epoll_wait收集發生事件的連接;

這樣只需要在進程啓動時建立1個epoll對象,並在需要的時候向它添加或刪除連接就可以了,因此,在實際收集事件時,epoll_wait的效率就會非常高,因爲調用epoll_wait時並沒有向它傳遞這100萬個連接,內核也不需要去遍歷全部的連接。

那麼Linux內核將如何實現以上的想法呢?下面以Linux內核2.6.35版本爲例,簡單說明一下epoll是如何高效處理事件的。

當某一進程調用epoll_create方法時,Linux內核會創建一個eventpoll結構體,這個結構體中有兩個成員與epoll的使用方式密切相關,如下所示:

struct eventpoll {

  ...

  /*紅黑樹的根節點,這棵樹中存儲着所有添加到epoll中的事件,也就是這個epoll監控的事件*/

  struct rb_root rbr;

  /*雙向鏈表rdllist保存着將要通過epoll_wait返回給用戶的、滿足條件的事件*/

  struct list_head rdllist;

  ...

};

每一個epoll對象都有一個獨立的eventpoll結構體,這個結構體會在內核空間中創造獨立的內存,用於存儲使用epoll_ctl方法向epoll對象中添加進來的事件。這些事件都會掛到rbr紅黑樹中,這樣重複添加的事件就可以通過紅黑樹而高效的識別出來(epoll_ctl方法很快)。

所有添加到epoll中的事件都會與設備(如網卡)驅動程序建立回調關係,也就是說相應事件的發生時會調用這裏的回調方法。這個回調方法在內核中叫做ep_poll_callback,它會把這樣的事件放到上面的rdllist雙向鏈表中。

在epoll中對於每一個事件都會建立一個epitem結構體,如下所示:

struct epitem {

  ...

  //紅黑樹節點

  struct rb_node rbn;

  //雙向鏈表節點

  struct list_head rdllink;

  //事件句柄等信息

  struct epoll_filefd ffd;

  //指向其所屬的eventepoll對象

  struct eventpoll *ep;

  //期待的事件類型

  struct epoll_event event;

  ...

};

這裏包含每一個事件對應着的信息。

當調用epoll_wait檢查是否有發生事件的連接時,只是檢查eventpoll對象中的rdllist雙向鏈表是否有epitem元素而已,如果rdllist鏈表不爲空,則這裏的事件複製到用戶態內存中,同時將事件數量返回給用戶。因此epoll_waitx效率非常高。epoll_ctl在向epoll對象中添加、修改、刪除事件時,從rbr紅黑樹中查找事件也非常快,也就是說epoll是非常高效的,它可以輕易地處理百萬級別的併發連接。

總結:

1)epoll_create創建一個epoll對象,該對象有一個eventpoll結構體,結構體中含有關鍵成員:存放epoll中的事件的紅黑樹根節點rbr、保存epoll_wait返回的事件的雙向鏈表rdllist;

2)epoll_ctl方法向epoll對象中註冊事件,將epitem添加到rbr中,事件與設備驅動程序建立回調關係,事件發生調用回調方法,將發生的事件添加到rdllist中;

3)epoll_wait檢查rdllist雙向鏈表;

4)與select和poll的區別:

select和poll每次收集事件時都會把所有的fd傳給操作系統內核,首先是用戶態內存到內核態內存的大量複製,然後系統內核輪詢所有fd有沒有未處理的事件,浪費資源。

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