Redis网络库源码分析(3)之ae.c

一、aeCreateEventLoop & aeCreateFileEvent

上一篇文章中,我们已经将服务器启动,只是其中有些细节我们跳过了,比如aeCreateEventLoop函数到底做了什么? 接下来我们要分析ae.c文件,它是整个Redis网络事件框架,其中定义了各个管理事件的函数,比如aeCreateFileEventaeDeleteFileEvent分别是注册新的事件和删除事件。

其实aeCreateEventLoop的作用主要是给server->loop申请空间。

//ae.c


aeEventLoop *aeCreateEventLoop(int setsize) {
    aeEventLoop *eventLoop; 

    if ((eventLoop = zmalloc(sizeof(*eventLoop))) == NULL) {    //给eventLoop申请空间
        goto err;
    }

    eventLoop->events = zmalloc(sizeof(aeFileEvent) * setsize); //给events链表申请空间
    eventLoop->fired = zmalloc(sizeof(aeFiredEvent) * setsize); //给fired链表申请空间
    if (eventLoop->events == NULL || eventLoop->fired == NULL) {
        goto err;
    }

    eventLoop->setsize = setsize;           //设置大小
    eventLoop->lastTime = time(NULL);       //设置lastTime=now
    eventLoop->timeEventHead = NULL;        //定时事件链表置空
    eventLoop->timeEventNextId = 0;         //定时事件的id为0
    eventLoop->stop = 0;                    //stop为0
    eventLoop->maxfd = -1;                  //最大文件描述符为0
    eventLoop->beforesleep = NULL;          //beforesleep设置为NULL

    if (aeApiCreate(eventLoop) == -1) {     //给EPOLL申请空间
        goto err;
    }

    /* Events with mask == AE_NONE are not set. So let's initialize the vector with it. */
    for (int i = 0; i < setsize; i++) {
        eventLoop->events[i].mask = AE_NONE; //将每一个fd的事件初始化为0
    }

    return eventLoop;

err:
    if (eventLoop) {
        zfree(eventLoop->events);
        zfree(eventLoop->fired);
        zfree(eventLoop);
    }

    return NULL;
}

至此申请空间完成,我们整个EventLoop结构如下图所示:

这里写图片描述

我们完成了所有空间的申请和初始化工作:

loop->events : 是一个aeFileEvent 数组,大小为 setsize 。
loop->fired :  是一个aeFiredEvent 数组,大小也为 setsize 。
loop->timeEventHead :目前为NULL
loop->apidata:指向aeApiState,包含epfd和epoll_event数组。

接着我们调用anetTcpServer返回了listen_fdanetTcpServer我们在anet.c分析的时候再说,接下来重点是我们调用aeCreateFileEventlisten_fd注册到epfd上的过程。

//ae.c


int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask, aeFileProc *proc, void *clientData) {
    if (fd >= eventLoop->setsize) {
        errno = ERANGE;
        return AE_ERR;
    }
    aeFileEvent *fe = &eventLoop->events[fd];  //利用fe指向eventLoop->events[listen_fd]

    if (aeApiAddEvent(eventLoop, fd, mask) == -1) { //本质是调用epoll_ctl(epfd,EPOLL_CTL_ADD,fd,...);
        return AE_ERR;
    }

    fe->mask |= mask;                          //如果fe->mask之前不是空,现在就相当于同时监控两个事件
    if (mask & AE_READABLE) {                
        fe->rfileProc = proc;                  //说明proc是读操作的处理函数
    }

    if (mask & AE_WRITABLE) {
        fe->wfileProc = proc;                  //说明proc是写操作的处理函数
    }

    fe->clientData = clientData;               //让它们指向同一个client或者server实例
    if (fd > eventLoop->maxfd) {
        eventLoop->maxfd = fd;                 //如果新的fd大于maxfd,则更新maxfd
    }

    return AE_OK;
}

此时我们的整个EventLoop变成了下面这个样子:

这里写图片描述

可以看到:

1loop->events[4].mask  = 1 , 表示读,rfileProc 为 acceptTcpHandler。 因为它是listen_fd,负责接受连接。为什么是 4 呢?因为 3 已经作为 epfd 的文件描述符了。

2 : 我们将 fd = 4 & EPOLLIN事件注册给了epfd。

现在就等着来新的连接了,因为这样的话一旦检测到listen_fd上有数据可读,那就会调用acceptTcpHandler接受连接,这也是回掉机制的一种体现:我们现在已经给listen_fd注册了相应的回掉函数了,等着事件发生,然后去调用注册好的函数。我们继续往下走继续看这个过程:

二、aeProcessEvents & acceptTcpHandler

继续向下会进入aeMain,之后一直轮询调用aeProcessEvents,接下来我们分析下aeProcessEvents到底是怎么处理各类事件的:

//ae.c


int aeProcessEvents(aeEventLoop *eventLoop, int flags) {
    int processed = 0, numevents;

    /* Nothing to do? return ASAP */
    if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) { 
        //如果flags什么事件都没有监听,return 0
        return 0;
    }

    /* 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. */
    /* 注意,我们即使没有文件事件,但是仍然想调用select/epoll,让其阻塞直到我们想处理的
     * 定时事件发生为止*/

    if (eventLoop->maxfd != -1 || ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) { 
        //如果有定时事件处理
        aeTimeEvent *shortest = NULL;
        struct timeval tv, *tvp;

        if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT)) {
            // for 循环查找到最近需要发生的定时事件
            shortest = aeSearchNearestTimer(eventLoop); 
        }

        if (shortest) {
            long now_sec, now_ms;
            aeGetTime(&now_sec, &now_ms);
            tvp = &tv;

            /* How many milliseconds we need to wait for the next time event to fire? */
            /* 计算我们需要等待的ms数,直到最近的定时事件发生*/
            long long ms = (shortest->when_sec - now_sec) * 1000 + shortest->when_ms - now_ms;


            if (ms > 0) {
                //如果定时事件没有过期,计算出需要等待的时间,作为epoll_wait的第四个参数
                tvp->tv_sec = ms / 1000;
                tvp->tv_usec = (ms % 1000) * 1000;
            } else {
                //否则置为0,epoll_wait就不会阻塞
                tvp->tv_sec = 0;
                tvp->tv_usec = 0;
            }
        } else {
            /* 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) {  //设置了AE_DONT_WAIT操作,就不等
                tv.tv_sec = tv.tv_usec = 0; 
                tvp = &tv;
            } else {                     //否则就阻塞等待直到事件发生  
                /* Otherwise we can block */
                tvp = NULL; /* wait forever */
            }
        }

        numevents = aeApiPoll(eventLoop, tvp);  //调用epoll_wait函数,返回需要处理的事件列表

        for (int i = 0; i < numevents; i++) {   //遍历依次处理loop->fired
            aeFileEvent *fe = &eventLoop->events[eventLoop->fired[i].fd];
            int mask = eventLoop->fired[i].mask;
            int fd = eventLoop->fired[i].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++;
        }
    }
    /* Check time events */
    /* 处理所有的时间事件 */
    if (flags & AE_TIME_EVENTS) {
        processed += processTimeEvents(eventLoop);
    }

    return processed; /* return the number of processed file/time events */
}

假设 listen_fd此时发生了事件,那一定是有新的连接过来,fe->rfileProc(eventLoop, fd, fe->clientData, mask) 就会使用 acceptTcpHandler接受连接:

static void acceptTcpHandler(aeEventLoop *loop, int fd, void *data, int mask)
{
    char cip[64];
    int cport;

    server_t *server = (server_t *)data;

    int cfd = anetTcpAccept(NULL, fd, cip, sizeof(cip), &cport); //调用accept接受连接
    if (cfd != -1) {
        printf("accepted ip %s:%d\n", cip, cport);
        anetNonBlock(NULL, cfd);                                 //设置socket非阻塞
        anetEnableTcpNoDelay(NULL, cfd);                         //开启TcpNoDelay选项
        client_t *client = alloc_client();                       //申请一个新的客户端
        if (!client) {
            printf("alloc client error...close socket\n");
            close(fd);
            return;
        }

        client->loop = loop;    
        client->fd = cfd;

        if (aeCreateFileEvent(loop, cfd, AE_READABLE, readEventHandler, client) == AE_ERR) {
            //继续调用aeCreateFileEvent给新连接的fd注册可读事件,并且注册读函数readEventHandler
            if (errno == ERANGE) {
                // or use aeResizeSetSize(server->loop, cfd) modify this limit
                printf("so many client, close new.");
            } else {
                printf("create socket readable event error, close it.");
            }
            free_client(client);
        }
    }
}

处理到这里,算是接受了一个连接,至于以后的读写操作,等到下次再分析~

posted on 2017-03-23 23:53  杨博东的博客  阅读(42)  评论(0编辑  收藏  举报

导航