標籤:
之前總結了redis的通訊流程,基本架構,epoll的封裝等等,這次介紹下
redis對於select模型的封裝
//select 模型typedef struct aeApiState { //讀檔案描述符集合,寫檔案描述符集合 fd_set rfds, wfds; /* We need to have a copy of the fd sets as it‘s not safe to reuse * FD sets after select(). */ //讀寫集合的副本 fd_set _rfds, _wfds;} aeApiState;
_rfds和_wfds是讀寫結合的副本,因為select調用後會將讀寫集合中未就緒的檔案描述符
清除,所以每次用_rfds和_wfds傳入,就不用擔心原讀寫集合描述符被清除。
封裝的基於select的初始化函數
static int aeApiCreate(aeEventLoop *eventLoop) { //開闢aeApiState空間 aeApiState *state = zmalloc(sizeof(aeApiState)); if (!state) return -1; //讀寫集合清零 FD_ZERO(&state->rfds); FD_ZERO(&state->wfds); eventLoop->apidata = state; return 0;}
函數將讀寫集合清零,並且將state回傳給eventloop的apidata部分。
記憶體回收功能
//釋放空間static void aeApiFree(aeEventLoop *eventLoop) { zfree(eventLoop->apidata);}
封裝的添加和刪除事件
//select 添加事件static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) { aeApiState *state = eventLoop->apidata; if (mask & AE_READABLE) FD_SET(fd,&state->rfds); if (mask & AE_WRITABLE) FD_SET(fd,&state->wfds); return 0;}//select 刪除事件static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) { aeApiState *state = eventLoop->apidata; if (mask & AE_READABLE) FD_CLR(fd,&state->rfds); if (mask & AE_WRITABLE) FD_CLR(fd,&state->wfds);}
添加事件函數將檔案描述根據mask是讀事件還是寫事件放入不同的set
刪除事件根據檔案描述符mask是讀事件還是寫事件從不同的set中清除
下面是核心功能,事件派發
//select 觸發事件static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) { aeApiState *state = eventLoop->apidata; int retval, j, numevents = 0; //將select讀集合的資料拷貝到_rfds memcpy(&state->_rfds,&state->rfds,sizeof(fd_set)); //將select寫集合資料拷貝到_wfds memcpy(&state->_wfds,&state->wfds,sizeof(fd_set)); //從讀和寫的copy集合裡選出就緒的檔案描述符 retval = select(eventLoop->maxfd+1, &state->_rfds,&state->_wfds,NULL,tvp); //大於零表示有就緒的檔案描述符 if (retval > 0) { //select的弊端所在,每次都要將所有的檔案描述符輪詢一遍 for (j = 0; j <= eventLoop->maxfd; j++) { int mask = 0; aeFileEvent *fe = &eventLoop->events[j]; if (fe->mask == AE_NONE) continue; //aeFileEvent 事件可讀 if (fe->mask & AE_READABLE && FD_ISSET(j,&state->_rfds)) mask |= AE_READABLE; //aeFileEvent 事件可寫 if (fe->mask & AE_WRITABLE && FD_ISSET(j,&state->_wfds)) mask |= AE_WRITABLE; eventLoop->fired[numevents].fd = j; eventLoop->fired[numevents].mask = mask; numevents++; } } return numevents;}
先將讀寫集合中的內容copy的_rfds和_wfds中,分別傳入select函數中,
這樣select後返回的_rfds中只有就緒的讀socket
_wfds中只有就緒的寫socket
通過FD_ISSET判斷讀寫事件之後放到eventloop的fire隊列裡。
基本的封裝就是這個樣子,select模型相對容易理解
我的公眾號:
對於redis底層架構的理解(五)