epoll是做為一個虛拟檔案系統來實作的,這樣做至少有以下兩個好處:
1、可以在核心裡維護一些資訊,這些資訊在多次epoll_wait間是保持的,比如所有受監控的檔案描述符。
2、epoll本身也可以被poll/epoll;
【1】epoll的實作中,所等待的裝置就緒後,便調用call_back函數,把該裝置加入到就緒隊列中,避免了像poll那樣裝置就緒後再次輪詢所有裝置找就緒者,由O(n)降到O(1)。
傳統的poll函數相當于每次調用都重起爐竈,從使用者空間完整讀入ufds,完成後再次完全拷貝到使用者空間,另外每次poll都需要對所有裝置做至少做一次加入和删除等待隊列操作,這些都是低效的原因。
3、epoll的實作代碼在fs/EventPoll.c中。【2】中最後簡略介紹了一下實作函數,可以看到,epoll等待隊列及調用函數。
Linux的等待隊列,實質上是回調函數隊列;【3】的後文,有epoll與poll等形象對比。
add_wait_queue(whead, &pwq->wait);
4、epoll儲存拷入的fd,通過epoll_ctl把所有fd傳入核心再一起"wait",這就省掉了不必要的重複拷貝。其次,在epoll_wait時,也不是把current輪流的加入fd對應的裝置等待隊列,而是在裝置等待隊列醒來時調用一個回調函數(當然,這就需要“喚醒回調”機制),把産生事件的fd歸入一個連結清單,然後傳回這個連結清單上的fd。
一個新建立的epoll檔案帶有一個struct eventpoll結構,這個結構上再挂一個紅黑樹,而這個紅黑樹就是每次epoll_ctl時fd存放的地方!【4,5】
5、建立struct eppoll_entry,設定其喚醒回調函數為ep_poll_callback,然後加入裝置等待隊列。隻有這樣,當裝置就緒,喚醒等待隊列上的等待fd時,ep_poll_callback就會被調用。每次調用poll系統調用,作業系統都要把current(目前程序)挂到fd對應的所有裝置的等待隊列上,可以想象,fd多到上千的時候,這樣“挂”法很費事;而每次調用epoll_wait則沒有這麼羅嗦,epoll隻在epoll_ctl時把current挂一遍(這第一遍是免不了的)并給每個fd一個指令“好了就調回調函數”,如果裝置有事件了,通過回調函數,會把fd放入rdllist,而每次調用epoll_wait就隻是收集rdllist裡的fd就可以了——epoll巧妙的利用回調函數,實作了更高效的事件驅動模型。
ep_poll_callback會幹什麼了——肯定是把紅黑樹上的收到event的epitem(代表每個fd)插入ep->rdllist中,這樣,當epoll_wait傳回時,rdllist裡就都是就緒的fd了!
比喻如下【3】
就像收本子的班長,以前(poll)得一個個學生地去問有沒有本子,如果沒有,它還得等待一段時間而後又繼續問(輪詢),現在好了,隻走一次(epoll_ctl),如果沒有本子,班長就告訴大家去那裡交本子(等待隊列,回調函數),當班長想起要取本子,就去那裡看看或者等待一定時間後離開(epoll_wait),有本子到了就叫醒他,然後取走。
6、linux系統的優點:everything is a file。
參考
【6】 講的詳細
<a href="http://observer.blog.hexun.com/31993204_d.html">http://observer.blog.hexun.com/31993204_d.html</a>