之前 我寫過一篇博客IO複用之select poll epoll 函數,大概介紹了一下epoll和select,poll的一些區別,接下來要 從原理剖析epoll的強大的原因
select和poll的工作機制
在某一時刻,進程收集有事件連接時,大部分的連接是沒有發生事件,但是 select和poll的工作機制,在 每次收集事件 的 時候,都把全部 的 連接的 套接字傳給操作系統(這首先就是用戶態內存到內核態內存的大量複製),同時由操作系統內核尋找這些連接上有沒有事件發生,將是一個很耗時的事情,所以 select和epoll只能處理幾千個併發連接(也受到操作系統文件描述符的限制)。
epoll的工作機制
首先epoll在Linux內核中申請了一個 簡易的文件系統,將一個 poll或select調用分成了3個部分:
- 調用
epoll_create
建立1個epoll對象(在epoll文件系統中 給這個 句柄 分配資源) - 調用
epoll_ctl
向epoll
添加大量的連接套接字 - 調用
epoll_wait
收集發生事件 的連接。
這樣,只需要在進程啓動的時候建立 1個epoll對象,並在需要的 時候向他添加或刪除連接 就可以,在實際收集事件時,epoll_wait的效率會非常的高,調用epoll_wait時並沒有向它傳遞全部的 連接,內核也不需要遍歷全部的 連接
詳解epoll_create方法
當一個進程調用epoll_create方法時,Linux內核會創建 一個eventpoll結構體,這個 結構體中有兩個 成員與epoll使用 的 方式密切相關
struct eventpoll{
/*紅黑樹的根節點,這顆樹存儲着所有添加到epoll的事件,也就是這個epoll監控 的事件*/
struct rb_root rbr;
// 雙向鏈表rallist保存着將要 通過 epoll_wait返回給用戶的、滿足條件的事件
struct list_head rdllist;
...
}
每一個epoll對象都有一個獨立的eventpoll結構體,這個 結構體會在內核 空間創造獨立 的內存,用於存儲使用epoll_ctl方法向epoll對象添加 進來的事件。這些事件都會 掛在rbr紅黑樹中,這樣重複添加的事件就 可以通過紅黑樹而高效地識別出來。所有添加到epoll的事件都會與設備(如網卡)驅動程序建立 回調關係,也就是說,相應的事件發生時會調用這裏的回調方法。這個 回調的方法 在內核叫做ep_poll_callback,它會把這樣的事件放在上面的rdllist雙向鏈表中。
在 epoll中,對於每個 事件 都會 建立一個epitem結構體
struct epitem
{
//紅黑樹節點
struct rb_node rbn;
//雙鏈 表節點
struct list_head rdllink;
//事件 句柄信息
struct epoll_filefd ffd;
//指向其所屬的eventpoll對象
struct eventpoll *ep;
// 期待 的事件類型
struct epoll_event event;
}
這裏 包含 每一個事件對應着的信息,當 調用epoll_wait檢查是否有 發生事件的連接時,只是檢查eventpoll對象中的rdllist雙向鏈表是否有epitem元素而已,如果rdllist鏈表不爲空,則把這裏的事件複製到用戶態 的 內存中,同時將事件數量返回給用戶。