epoll工作原理
最近在讀張彥飛編寫的《深入理解Linux網(wǎng)絡(luò)》,這本書講得非常深,其中包含了大量linux底層源碼。讀的過程很痛苦,但對我而言哪怕只讀懂了其中皮毛也能受益匪淺。
這篇文章就是在學(xué)習(xí)到epoll底層原理時的筆記記錄
實現(xiàn)
epoll_create:創(chuàng)建一個結(jié)構(gòu)為struct eventpoll的內(nèi)核對象
epoll_ctl:向epoll對象添加要管理的socket連接
epoll_wait:等待epoll對象管理的socket連接上的IO事件
epoll內(nèi)核對象的創(chuàng)建
調(diào)用epoll_create()函數(shù)時,內(nèi)核會創(chuàng)建一個struct eventpoll結(jié)構(gòu)的內(nèi)核對象
wq:等待隊列鏈表,數(shù)據(jù)就緒的時候通過wq找到阻塞在epoll對象上的用戶進程
rbr:紅黑樹,支持大量socket連接的高效查找、插入、刪除。通過紅黑樹管理添加進來的所有socket連接
rdllist:就緒鏈表,用于存放就緒的socket連接,不用遍歷紅黑樹,只用在rdllist中就能找到就緒連接
為epoll對象添加socket
調(diào)用epoll_ctl()函數(shù)時,配合EPOLL_CTL_ADD參數(shù)可以向epoll對象添加socket
創(chuàng)建紅黑樹節(jié)點對象epitem
將等待事件添加到socket的等待隊列中,設(shè)置數(shù)據(jù)就緒的回調(diào)函數(shù)為ep_poll_callback
將epitem插入rbr紅黑樹
這里和同步阻塞的區(qū)別是,同步阻塞時軟中斷將數(shù)據(jù)收到socket接收隊列后,會喚醒在等待隊列中阻塞的進程。而epoll方式時軟中斷將數(shù)據(jù)收到socket接收隊列后,會通過這個ep_poll_callback回調(diào),通知epoll對象
等待接收
調(diào)用epoll_wait()函數(shù)時,觀察eventpoll→rdllist就緒鏈表里有沒有數(shù)據(jù),有數(shù)據(jù)就返回,沒數(shù)據(jù)就把當(dāng)前進程添加到eventpoll→wq等待隊列鏈表中,設(shè)置等待項回調(diào)函數(shù)default_wake_function,然后阻塞自己。
socket的等待隊列項:回調(diào)函數(shù)為ep_poll_callback,private為null
eventpoll的等待隊列項:回調(diào)函數(shù)為default_wake_function,private指向等待該事件的用戶進程
接收數(shù)據(jù)過程:
系統(tǒng)線程ksoftirqd根據(jù)skb包頭中的IP、端口信息找到對應(yīng)的socket內(nèi)核對象
將處理后的包數(shù)據(jù)放入socket的接收隊列中
調(diào)用socket的等待隊列中等待項設(shè)置的回調(diào)函數(shù)ep_poll_callback
在ep_poll_callback內(nèi)部會執(zhí)行以下過程:
根據(jù)等待隊列項找到epitem,進而找到eventpoll對象
將epitem添加到eventpoll→rdllist就緒隊列中,查看eventpoll→wq等待隊列是否有等待項
如果eventpoll→wq等待隊列中沒有等待項,說明用戶進程并未阻塞,此時軟中斷結(jié)束
如果eventpoll→wq等待隊列中有等待項,則調(diào)用其回調(diào)函數(shù)default_wake_function喚醒用戶進程
總結(jié)
兩個不同的回調(diào)函數(shù):
ep_poll_callback:調(diào)用epoll_ctl()函數(shù)時設(shè)置到socket對象上
default_wake_function:調(diào)用epoll_wait()函數(shù)時設(shè)置到epoll對象上
