前面讲过evport/epoll/kqueue的实现,下来我们继续看看我们select在redis中的使用
1:首先通过FD_ZERO来清空用于监控读和写fd的rfds和wfds的链表
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;
static int aeApiCreate(aeEventLoop *eventLoop) {
aeApiState *state = zmalloc(sizeof(aeApiState));
if (!state) return -1;
FD_ZERO(&state->rfds);
FD_ZERO(&state->wfds);
eventLoop->apidata = state;
return 0;
}
2:通过FD_SET 将要监控fd对应的bit为写道读事件state->rfds 或者写事件state->wfds
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;
}
3:通过select 监控fd
static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
aeApiState *state = eventLoop->apidata;
int retval, j, numevents = 0;
#将要监控的fd的copy到临时变量中
memcpy(&state->_rfds,&state->rfds,sizeof(fd_set));
memcpy(&state->_wfds,&state->wfds,sizeof(fd_set));
#通过select来监控读列表state->_rfds和写列表state->_wfds,这里的tvp 表示timeout
retval = select(eventLoop->maxfd+1,
&state->_rfds,&state->_wfds,NULL,tvp);
#retval 返回值大于零,说明select监控到的读或者写时间而非timeout
if (retval > 0) {
#正确监控到的读或者写时间,遍历eventLoop->events 来得到监控事件
for (j = 0; j <= eventLoop->maxfd; j++) {
int mask = 0;
aeFileEvent *fe = &eventLoop->events[j];
#如果对应的fd没有读写时间则继续,这也是select效率底下的原因之一。
if (fe->mask == AE_NONE) continue;
#一下两个if分别使用FD_ISSET来判断对应的fd有事件发生,并通过mask来判断是读事件还是写事件
if (fe->mask & AE_READABLE && FD_ISSET(j,&state->_rfds))
mask |= AE_READABLE;
if (fe->mask & AE_WRITABLE && FD_ISSET(j,&state->_wfds))
mask |= AE_WRITABLE;
#保持发生事件的fd和mask
eventLoop->fired[numevents].fd = j;
eventLoop->fired[numevents].mask = mask;
numevents++;
}
}
return numevents;
}
redis中io复用之select
最新推荐文章于 2024-01-13 09:18:31 发布