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