自己動(dòng)手實(shí)現(xiàn)Epoll

Epoll是Linux IO多路復(fù)用的管理機(jī)制。作為現(xiàn)在Linux平臺(tái)高性能網(wǎng)絡(luò)IO必要的組件。內(nèi)核的實(shí)現(xiàn)可以參照:fs/eventpoll.c .

為什么需要自己實(shí)現(xiàn)epoll呢?現(xiàn)在自己打算做一個(gè)用戶態(tài)的協(xié)議棧。采用單線程的模式。https://github.com/wangbojing/NtyTcp,至于為什么要實(shí)現(xiàn)用戶態(tài)協(xié)議棧?可以自行百度C10M的問(wèn)題。

由于協(xié)議棧做到了用戶態(tài)故需要自己實(shí)現(xiàn)高性能網(wǎng)絡(luò)IO的管理。所以epoll就自己實(shí)現(xiàn)一下。代碼:https://github.com/wangbojing/NtyTcp/blob/master/src/nty_epoll_rb.c

在實(shí)現(xiàn)epoll之前,先得好好理解內(nèi)核epoll的運(yùn)行原理。內(nèi)核的epoll可以從四方面來(lái)理解。

1.?Epoll的數(shù)據(jù)結(jié)構(gòu),rbtree對(duì)的存儲(chǔ),ready隊(duì)列存儲(chǔ)就緒io。

2.?Epoll的線程安全,SMP的運(yùn)行,以及防止死鎖。

3.?Epoll內(nèi)核回調(diào)。

4.?Epoll的LT(水平觸發(fā))與ET(邊沿觸發(fā))

下面從這四個(gè)方面來(lái)實(shí)現(xiàn)epoll。

一、Epoll數(shù)據(jù)結(jié)構(gòu)

Epoll主要由兩個(gè)結(jié)構(gòu)體:eventpoll與epitem。Epitem是每一個(gè)IO所對(duì)應(yīng)的的事件。比如?epoll_ctl EPOLL_CTL_ADD操作的時(shí)候,就需要?jiǎng)?chuàng)建一個(gè)epitem。Eventpoll是每一個(gè)epoll所對(duì)應(yīng)的的。比如epoll_create?就是創(chuàng)建一個(gè)eventpoll。

Epitem的定義

Eventpoll的定義

數(shù)據(jù)結(jié)構(gòu)如下圖所示。

List?用來(lái)存儲(chǔ)準(zhǔn)備就緒的IO。對(duì)于數(shù)據(jù)結(jié)構(gòu)主要討論兩方面:insert與remove。同樣如此,對(duì)于list我們也討論insert與remove。何時(shí)將數(shù)據(jù)插入到list中呢?當(dāng)內(nèi)核IO準(zhǔn)備就緒的時(shí)候,則會(huì)執(zhí)行epoll_event_callback的回調(diào)函數(shù),將epitem添加到list中。

那何時(shí)刪除list中的數(shù)據(jù)呢?當(dāng)epoll_wait激活重新運(yùn)行的時(shí)候,將list的epitem逐一copy到events參數(shù)中。

Rbtree用來(lái)存儲(chǔ)所有io的數(shù)據(jù),方便快速通io_fd查找。也從insert與remove來(lái)討論。

對(duì)于rbtree何時(shí)添加:當(dāng)App執(zhí)行epoll_ctl EPOLL_CTL_ADD操作,將epitem添加到rbtree中。何時(shí)刪除呢?當(dāng)App執(zhí)行epoll_ctl EPOLL_CTL_DEL操作,將epitem添加到rbtree中。

List與rbtree的操作又如何做到線程安全,SMP,防止死鎖呢?

二、Epoll鎖機(jī)制

Epoll?從以下幾個(gè)方面是需要加鎖保護(hù)的。List的操作,rbtree的操作,epoll_wait的等待。

List使用最小粒度的鎖spinlock,便于在SMP下添加操作的時(shí)候,能夠快速操作list。

List添加

346行:獲取spinlock。

347行:epitem?的rdy置為1,代表epitem已經(jīng)在就緒隊(duì)列中,后續(xù)再觸發(fā)相同事件就只需更改event。

348行:添加到list中。

349行:將eventpoll的rdnum域?加1。

350行:釋放spinlock

List刪除

301行:獲取spinlock

304行:判讀rdnum與maxevents的大小,避免event溢出。

307行:循環(huán)遍歷list,判斷添加list不能為空

309行:獲取list首個(gè)結(jié)點(diǎn)

310行:移除list首個(gè)結(jié)點(diǎn)。

311行:將epitem的rdy域置為0,標(biāo)識(shí)epitem不再就緒隊(duì)列中。

313行:copy epitem的event到用戶空間的events。

316行:copy數(shù)量加1

317行:eventpoll中rdnum減一。

避免SMP體系下,多核競(jìng)爭(zhēng)。此處采用自旋鎖,不適合采用睡眠鎖。

Rbtree的添加

149行:獲取互斥鎖。

153行:查找sockid的epitem是否存在。存在則不能添加,不存在則可以添加。

160行:分配epitem。

167行:sockid賦值

168行:將設(shè)置的event添加到epitem的event域。

170行:將epitem添加到rbrtree中。

173行:釋放互斥鎖。

Rbtree刪除:

177行:獲取互斥鎖。

181行:刪除sockid的結(jié)點(diǎn),如果不存在,則rbtree返回-1。

188行:釋放epitem

190行:釋放互斥鎖。

Epoll_wait的掛起。

采用pthread_cond_wait,具體實(shí)現(xiàn)可以參照。

https://github.com/wangbojing/NtyTcp/blob/master/src/nty_epoll_rb.c

三、Epoll回調(diào)

Epoll?的回調(diào)函數(shù)何時(shí)執(zhí)行,此部分需要與Tcp的協(xié)議棧一起來(lái)闡述。Tcp協(xié)議棧的時(shí)序圖如下圖所示,epoll從協(xié)議棧回調(diào)的部分從下圖的編號(hào)1,2,3,4。具體Tcp協(xié)議棧的實(shí)現(xiàn),后續(xù)從另外的文章中表述出來(lái)。下面分別對(duì)四個(gè)步驟詳細(xì)描述

編號(hào)1:是tcp三次握手,對(duì)端反饋ack后,socket進(jìn)入rcvd狀態(tài)。需要將監(jiān)聽(tīng)socket的event置為EPOLLIN,此時(shí)標(biāo)識(shí)可以進(jìn)入到accept讀取socket數(shù)據(jù)。

編號(hào)2:在established狀態(tài),收到數(shù)據(jù)以后,需要將socket的event置為EPOLLIN狀態(tài)。

編號(hào)3:在established狀態(tài),收到fin時(shí),此時(shí)socket進(jìn)入到close_wait。需要socket的event置為EPOLLIN。讀取斷開(kāi)信息。

編號(hào)4:檢測(cè)socket的send狀態(tài),如果對(duì)端cwnd>0是可以,發(fā)送的數(shù)據(jù)。故需要將socket置為EPOLLOUT。

所以在此四處添加EPOLL的回調(diào)函數(shù),即可使得epoll正常接收到io事件。

四、LT與ET

LT(水平觸發(fā))與ET(邊沿觸發(fā))是電子信號(hào)里面的概念。不清楚可以man epoll查看的。如下圖所示:

比如:event = EPOLLIN | EPOLLLT,將event設(shè)置為EPOLLIN與水平觸發(fā)。只要event為EPOLLIN時(shí)就能不斷調(diào)用epoll回調(diào)函數(shù)。

比如: event = EPOLLIN | EPOLLET,event如果從EPOLLOUT變化為EPOLLIN的時(shí)候,就會(huì)觸發(fā)。在此情形下,變化只發(fā)生一次,故只調(diào)用一次epoll回調(diào)函數(shù)。關(guān)于水平觸發(fā)與邊沿觸發(fā)放在epoll回調(diào)函數(shù)執(zhí)行的時(shí)候,如果為EPOLLET(邊沿觸發(fā)),與之前的event對(duì)比,如果發(fā)生改變則調(diào)用epoll回調(diào)函數(shù),如果為EPOLLLT(水平觸發(fā)),則查看event是否為EPOLLIN,即可調(diào)用epoll回調(diào)函數(shù)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容