epoll大致結構

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

       這裏有一個分廠明顯的問題,即在某一時刻,進程收集有事件的連接時,其實這100萬連接中的大部分都是沒有事件發生的。因此,如果每次收集事件時,都把這100萬連接的套接字傳給操作系統(這首先就是用戶態內存到內核態內存的大量複製),而由操作系統內核尋找這些連接上有沒有未處理的事件,將會是巨大的資源浪費,然而select和poll就是這樣做的,因此他們最多隻能處理幾千個併發連接。而epoll不這樣做,他在linux內核中申請了一個簡易的文件系統,把原先的一個select或者poll調用分成了3個部分:調用epoll_create建立1個epoll對象(在epoll文件系統中給這個句柄分配資源)、調用epoll_ctl向epoll對象中添加這100萬個連接的套接字、調用epoll_wati收集發生事件的連接。這樣,只需要在進程啓動時建立1個epoll對象,並在需要的時候向它添加或刪除連接就可以了,因此,在實際收集事件時,epoll_wait的效率就會非常高,因爲調用epoll_wait時並沒有向它傳遞着100萬個連接,內核也不需要去遍歷全部的連接。

      介紹epoll是怎麼處理這種情況的

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

     struct   eventpoll{

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

      struct rb_root_rbr;

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

     struct  list_head_rdllist;


}

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

clip_image002

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

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


轉載:http://blog.csdn.net/yusiguyuan/article/details/24833339

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