redis原始碼解析----epoll的使用
redis原始碼解析----epoll的使用
平時做專案,涉及到網路層的都是epoll,前幾年發現redis的epoll實現起來非常的精簡,好用。因為提供的介面簡單,愛並實現的很高效。於是,我就提取出來,直接使用。
今天又開啟該檔案詳細的看看他的實現細節。
首先簡單介紹epoll,它是linux核心下的一個高效的處理大批量的檔案操作符的一個實現。不僅限於socket fd。
他在超時時間內會喚醒有事件的操作符。其中有兩種模式 1、水平觸發(Level Triggered)2、邊緣觸發(Edge Triggered)
簡單概括這兩種,水平觸發是是預設的工作方式,並且同時支援block和no-block socket.在這種做法中,核心告訴你一個檔案描述符是否就緒了,然後你可以對這個就緒的fd進行IO操作。如果你不作任何操作,核心還是會繼續通知你的,所以,這種模式程式設計出錯誤可能性要小一點。傳統的select/poll都是這種模型的代表。
而邊緣模式是 有讀寫等事件,只會通知你一次,直到下一次事件再一次觸發。所以,使用該模式的時候,一般情況下比較複雜,要對操作符讀取資料到完全為空。才能保證資料不會丟失
epoll 提供了三個介面,
首先通過epoll_create(int maxfds)來建立一個epoll的控制程式碼
之後在你的網路主迴圈裡面,每一幀的呼叫epoll_wait(int epfd, epoll_event *events, int max events, int timeout)來查詢所有的網路介面,看哪一個可以讀,哪一個可以寫了。
epoll_ctl用來新增/修改/刪除需要偵聽的檔案描述符及其事件。
好了,當我們瞭解瞭如何使用這三個函式後,redis ae 做得就是如何友好的使用這三個函式了,並給我們提供方面的介面,讓我們只關注 資料包的處理。
首先了解一下ae的結構體eventloop
/* State of an event based program */
typedef struct aeEventLoop {
int maxfd; /* highest file descriptor currently registered */
int setsize; /* max number of file descriptors tracked */
long long timeEventNextId;
time_t lastTime; /* Used to detect system clock skew */
aeFileEvent *events; /* Registered events */
aeFiredEvent *fired; /* Fired events */
aeTimeEvent *timeEventHead;
int stop;
void *apidata; /* This is used for polling API specific data */
aeBeforeSleepProc *beforesleep;
} aeEventLoop;
我們首先只關注epoll相關,maxfd,表示能夠註冊的最大操作符數,也就是aeFileEvent *events的最大陣列,
int setsize; /* max number of file descriptors tracked */
同上,能夠分配的最大陣列的數量。events 成員儲存了我們要註冊到epoll裡的操作符,以及對該操作符事件到來的時候進行的操作的相關函式,具體看一下起結構體我們就明白了。
/* File event structure */
typedef struct aeFileEvent {
int mask; /* one of AE_(READABLE|WRITABLE) */
aeFileProc *rfileProc;
aeFileProc *wfileProc;
void *clientData;
} aeFileEvent;
/* A fired event */
typedef struct aeFiredEvent {
int fd;
int mask;
} aeFiredEvent;
retval = epoll_wait(state->epfd,state->events,eventLoop->setsize,
tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1);
//儲存觸發事件的某個檔案描述符相關的資料(與具體使用方式有關)
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
//感興趣的事件和被觸發的事件
struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
typedef struct aeApiState {
int epfd;
struct epoll_event *events;
} aeApiState;
static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
aeApiState *state = eventLoop->apidata;
int retval, numevents = 0;
retval = epoll_wait(state->epfd,state->events,eventLoop->setsize,
tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1);
if (retval > 0) {
int j;
numevents = retval;
for (j = 0; j < numevents; j++) {
int mask = 0;
struct epoll_event *e = state->events+j;
if (e->events & EPOLLIN) mask |= AE_READABLE;
if (e->events & EPOLLOUT) mask |= AE_WRITABLE;
if (e->events & EPOLLERR) mask |= AE_WRITABLE;
if (e->events & EPOLLHUP) mask |= AE_WRITABLE;
eventLoop->fired[j].fd = e->data.fd;
eventLoop->fired[j].mask = mask;
}
}
return numevents;
}
numevents = aeApiPoll(eventLoop, tvp);
for (j = 0; j < numevents; j++) {
aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];
int mask = eventLoop->fired[j].mask;
int fd = eventLoop->fired[j].fd;
int rfired = 0;
/* note the fe->mask & mask & ... code: maybe an already processed
* event removed an element that fired and we still didn't
* processed, so we check if the event is still valid. */
if (fe->mask & mask & AE_READABLE) {
rfired = 1;
fe->rfileProc(eventLoop,fd,fe->clientData,mask);
}
if (fe->mask & mask & AE_WRITABLE) {
if (!rfired || fe->wfileProc != fe->rfileProc)
fe->wfileProc(eventLoop,fd,fe->clientData,mask);
}
processed++;
}
}
相關文章
- epoll–原始碼剖析原始碼
- Redis radix tree原始碼解析Redis原始碼
- 從linux原始碼看epollLinux原始碼
- TextWatcher的使用及原始碼解析原始碼
- Redis原始碼解析之跳躍表(一)Redis原始碼
- Redis原始碼解析之跳躍表(三)Redis原始碼
- Redis(五):hash/hset/hget 命令原始碼解析Redis原始碼
- redis自學(18)epollRedis
- GYHttpMock:使用及原始碼解析HTTPMock原始碼
- [原始碼解析] PyTorch 如何使用GPU原始碼PyTorchGPU
- PyTorch ResNet 使用與原始碼解析PyTorch原始碼
- DialogFragment使用到原始碼完全解析Fragment原始碼
- 【Redis原始碼】Redis 6 ACL原始碼詳解Redis原始碼
- linux系統下poll和epoll核心原始碼剖析Linux原始碼
- SpringAOP的原始碼解析Spring原始碼
- redux的原始碼解析Redux原始碼
- Webpack原始碼基礎-Tapable從使用Hook到原始碼解析Web原始碼Hook
- Redis 原始碼解析之通用雙向連結串列(adlist)Redis原始碼
- 曹工說Redis原始碼(6)-- redis server 主迴圈大體流程解析Redis原始碼Server
- epoll使用與原理
- spark的基本運算元使用和原始碼解析Spark原始碼
- Android 原始碼分析之 EventBus 的原始碼解析Android原始碼
- 【原始碼解析】- ArrayList原始碼解析,絕對詳細原始碼
- Java Timer原始碼解析(定時器原始碼解析)Java原始碼定時器
- Spring @Profile註解使用和原始碼解析Spring原始碼
- PureComponent 使用注意事項以及原始碼解析原始碼
- OkHttp 開源庫使用與原始碼解析HTTP原始碼
- 雨露均沾的OkHttp—WebSocket長連線的使用&原始碼解析HTTPWeb原始碼
- 曹工說Redis原始碼(5)-- redis server 啟動過程解析,以及EventLoop每次處理事件前的前置工作解析(下)Redis原始碼ServerOOP事件
- ReactNative原始碼解析-初識原始碼React原始碼
- 在Deno中使用Redis的教程和原始碼 -LogRocket部落格Redis原始碼
- Hive原始碼解析Hive原始碼
- ArrayList原始碼解析原始碼
- ConcurrentHashMap原始碼解析HashMap原始碼
- LinkedBlockingQueue 原始碼解析BloC原始碼
- Axios原始碼解析iOS原始碼
- RecyclerView原始碼解析View原始碼
- MyBatis原始碼解析MyBatis原始碼