主要分析 redis server,對應文件redis.h/redis.c,從main函數開始分析redis server的啓動過程。
主要分析以下幾個函數
initServerConfig():解析配置等
initServer():初始化工作
aeSetBeforeSleepProc(server.el,beforeSleep):每次進入事件輪循前都會執行[TOREAD]
aeMain(server.el):事件輪循,運行事件處理器,一直到服務器關閉爲止
aeDeleteEventLoop(server.el):服務器關閉,停止事件輪循
aeMain(server.el)
/*
* 事件處理器的主循環
*/
void aeMain(aeEventLoop *eventLoop) {
eventLoop->stop = 0;
while (!eventLoop->stop) {
// 如果有需要在事件處理前執行的函數,那麼運行它
if (eventLoop->beforesleep != NULL)
eventLoop->beforesleep(eventLoop);
// 開始處理事件
aeProcessEvents(eventLoop, AE_ALL_EVENTS);
}
}
大致流程:
- beforesleep:在處理事件前要執行的函數,處理被阻塞命令
- aeProcessEvents:處理定時事件和網絡io事件
- redis server啓動完畢,等待客戶端請求
事件處理器狀態結構體aeEventLoop定義如下:
/* State of an event based program
*
* 事件處理器的狀態
*/
typedef struct aeEventLoop {
// 目前已註冊的最大描述符
int maxfd; /* highest file descriptor currently registered */
// 目前已追蹤的最大描述符
int setsize; /* max number of file descriptors tracked */
// 用於生成時間事件 id
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;
int aeProcessEvents(aeEventLoop *eventLoop, int flags)
/* Note that we want call select() even if there are no
* file events to process as long as we want to process time
* events, in order to sleep until the next time event is ready
* to fire. */
if (eventLoop->maxfd != -1 ||
((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {
int j;
aeTimeEvent *shortest = NULL;
struct timeval tv, *tvp;
// 獲取最近的時間事件
if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))
shortest = aeSearchNearestTimer(eventLoop);
if (shortest) {
如果時間事件存在的話,那麼根據最近可執行時間事件和現在時間的時間差來決定文件事件的阻塞時間。計算距今最近的時間事件還要多久才能達到,並將該時間距保存在 tv 結構中:
aeGetTime(&now_sec, &now_ms);
tvp = &tv;
tvp->tv_sec = shortest->when_sec - now_sec;
if (shortest->when_ms < now_ms) {
tvp->tv_usec = ((shortest->when_ms+1000) - now_ms)*1000;
tvp->tv_sec --;
} else {
tvp->tv_usec = (shortest->when_ms - now_ms)*1000;
}
// 時間差小於 0 ,說明事件已經可以執行了,將秒和毫秒設爲 0 (不阻塞)
if (tvp->tv_sec < 0) tvp->tv_sec = 0;
if (tvp->tv_usec < 0) tvp->tv_usec = 0;
} else {
如果時間事件不存在:
// 根據 AE_DONT_WAIT 是否設置來決定是否阻塞,以及阻塞的時間長度
/* If we have to check for events but need to return
* ASAP because of AE_DONT_WAIT we need to set the timeout
* to zero */
if (flags & AE_DONT_WAIT) {
// 設置文件事件不阻塞
tv.tv_sec = tv.tv_usec = 0;
tvp = &tv;
} else {
/* Otherwise we can block */
// 文件事件可以阻塞直到有事件到達爲止
tvp = NULL; /* wait forever */
}
處理文件事件,阻塞時間由tvp決定:
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 確保讀/寫事件只能執行其中一個
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++;
}
// 執行時間事件
if (flags & AE_TIME_EVENTS)
processed += processTimeEvents(eventLoop);
return processed; /* return the number of processed file/time events */
}