標籤:blog http 使用 檔案 2014 art ar 問題
設想一個情境:有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方法會非常快)。
全部加入?到epoll中的事件都會與裝置(如網卡)驅動程式建立回調關係,也就是說,相應的事件發生時會調用這裡的回調方法。這個回調方法在核心中叫做ep_epoll_callback,它會把這種事件放到上面的rdllist雙向鏈表中。在epoll中,對於每個事件都會建立一個epitem結構體。這裡包括每個事件相應著的資訊。
當調用epoll_wait檢查是否有發生事件的串連時,僅僅是檢查eventpoll對象中的rdllist雙向鏈表是否有epitem元素而已,假設rdllist鏈表不為空白,則把這裡的事件拷貝到使用者態記憶體中,同一時候將時間數量返回給使用者,因此,epoll_wait的效率很高,epoll_ctl在向epoll對象中加入?、改動。刪除事件時,從rbr紅/黑樹狀結構中尋找事件也很快,也就是說,epoll是很高效的,它能夠輕易地處理百萬級的並發串連。