本文只介紹epoll的主要流程而不是分析源代碼,如果需要了解更多的細節可以自己翻閱相關的內核源代碼.
相關內核代碼:
fs/eventpoll.c
判斷一個tcp套接字上是否有激活事件:net/ipv4/tcp.c:tcp_poll函數
每個epollfd在內核中有一個對應的eventpoll結構對象.其中關鍵的成員是一個readylist(eventpoll:rdllist)
和一棵紅黑樹(eventpoll:rbr).
一個fd被添加到epoll中之后(EPOLL_ADD),內核會為它生成一個對應的epitem結構對象.epitem被添加到
eventpoll的紅黑樹中.紅黑樹的作用是使用者調用EPOLL_MOD的時候可以快速找到fd對應的epitem。
調用epoll_wait的時候,將readylist中的epitem出列,將觸發的事件拷貝到用戶空間.之后判斷epitem是否需
要重新添加回readylist.
epitem重新添加到readylist必須滿足下列條件:
1) epitem上有用戶關注的事件觸發.
2) epitem被設置為水平觸發模式(如果一個epitem被設置為邊界觸發則這個epitem不會被重新添加到readylist
中,在什么時候重新添加到readylist請繼續往下看).
注意,如果epitem被設置為EPOLLONESHOT模式,則當這個epitem上的事件拷貝到用戶空間之后,會將
這個epitem上的關注事件清空(只是關注事件被清空,並沒有從epoll中刪除,要刪除必須對那個描述符調用
EPOLL_DEL),也就是說即使這個epitem上有觸發事件,但是因為沒有用戶關注的事件所以不會被重新添加到
readylist中.
epitem被添加到readylist中的各種情況(當一個epitem被添加到readylist如果有線程阻塞在epoll_wait中,那
個線程會被喚醒):
1)對一個fd調用EPOLL_ADD,如果這個fd上有用戶關注的激活事件,則這個fd會被添加到readylist.
2)對一個fd調用EPOLL_MOD改變關注的事件,如果新增加了一個關注事件且對應的fd上有相應的事件激活,
則這個fd會被添加到readylist.
3)當一個fd上有事件觸發時(例如一個socket上有外來的數據)會調用ep_poll_callback(見eventpoll::ep_ptable_queue_proc),
如果觸發的事件是用戶關注的事件,則這個fd會被添加到readylist中.
了解了epoll的執行過程之后,可以回答一個在使用邊界觸發時常見的疑問.在一個fd被設置為邊界觸發的情況下,
調用read/write,如何正確的判斷那個fd已經沒有數據可讀/不再可寫.epoll文檔中的建議是直到觸發EAGAIN
錯誤.而實際上只要你請求字節數小於read/write的返回值就可以確定那個fd上已經沒有數據可讀/不再可寫.
最后用一個epollfd監聽另一個epollfd也是合法的,epoll通過調用eventpoll::ep_eventpoll_poll來判斷一個
epollfd上是否有觸發的事件(只能是讀事件).