为什么 Redis 单线程能支撑高并发?( 二 )

这些上下文信息会存储在 eventLoop 的 void *state 中 , 不会暴露到上层 , 只在当前子模块中使用 。
封装 select 函数select 可以监控 FD 的可读、可写以及出现错误的情况 。
在介绍 I/O 多路复用模块如何对 select 函数封装之前 , 先来看一下 select 函数使用的大致流程:
int fd = /* file descriptor */fd_set rfds;FD_ZERO(FD_SET(fd,; ) {select(fd+1,if (FD_ISSET(fd,--tt-darkmode-color: #A3A3A3;">初始化一个可读的 fd_set 集合 , 保存需要监控可读性的 FD;

  • 使用 FD_SET 将 fd 加入 rfds;
  • 调用 select 方法监控 rfds 中的 FD 是否可读;
  • 当 select 返回时 , 检查 FD 的状态并完成对应的操作 。
  • 【为什么 Redis 单线程能支撑高并发?】而在 Redis 的 ae_select 文件中代码的组织顺序也是差不多的 , 首先在 aeApiCreate 函数中初始化 rfds 和 wfds:
    static int aeApiCreate(aeEventLoop *eventLoop) {aeApiState *state = zmalloc(sizeof(aeApiState));if (!state) return -1;FD_ZERO(FD_ZERO(eventLoop->apidata = http://kandian.youth.cn/index/state;return 0;}而 aeApiAddEvent 和 aeApiDelEvent 会通过 FD_SET 和 FD_CLR 修改 fd_set 中对应 FD 的标志位:
    static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {aeApiState *state = eventLoop->apidata;if (maskif (maskreturn 0;}整个 ae_select 子模块中最重要的函数就是 aeApiPoll , 它是实际调用 select 函数的部分 , 其作用就是在 I/O 多路复用函数返回时 , 将对应的 FD 加入 aeEventLoop 的 fired 数组中 , 并返回事件的个数:
    static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {aeApiState *state = eventLoop->apidata;int retval, j, numevents = 0;memcpy(memcpy(retval = select(eventLoop->maxfd+1,if (retval > 0) {for (j = 0; j <= eventLoop->maxfd; j++) {int mask = 0;aeFileEvent *fe =if (fe->mask == AE_NONE) continue;if (fe->maskif (fe->maskeventLoop->fired[numevents].fd = j;eventLoop->fired[numevents].mask = mask;numevents++;}}return numevents;}封装 epoll 函数Redis 对 epoll 的封装其实也是类似的 , 使用 epoll_create 创建 epoll 中使用的 epfd:
    static int aeApiCreate(aeEventLoop *eventLoop) {aeApiState *state = zmalloc(sizeof(aeApiState));if (!state) return -1;state->events = zmalloc(sizeof(struct epoll_event)*eventLoop->setsize);if (!state->events) {zfree(state);return -1;}state->epfd = epoll_create(1024); /* 1024 is just a hint for the kernel */if (state->epfd == -1) {zfree(state->events);zfree(state);return -1;}eventLoop->apidata = http://kandian.youth.cn/index/state;return 0;}在 aeApiAddEvent 中使用 epoll_ctl 向 epfd 中添加需要监控的 FD 以及监听的事件:
    static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {aeApiState *state = eventLoop->apidata;struct epoll_event ee = {0}; /* avoid valgrind warning *//* If the fd was already monitored for some event, we need a MOD* operation. Otherwise we need an ADD operation. */int op = eventLoop->events[fd].mask == AE_NONE ?EPOLL_CTL_ADD : EPOLL_CTL_MOD;ee.events = 0;mask |= eventLoop->events[fd].mask; /* Merge old events */if (maskif (maskee.data.fd = fd;if (epoll_ctl(state->epfd,op,fd,return 0;}