libevent事件(三)---event_add和epoll_add

来源:互联网 发布:专业检测手机信号软件 编辑:程序博客网 时间:2024/05/29 17:54

libevent将定时事件、信号、描述符事件都集成到event中,并且分别维护了相关的链表。
因此,event_addevent_delete主要是操作链表,并且调用底层的api。

1. event_add函数

首先是超时事件,timer事件用一个小根堆来表示,这个堆中的最小时间可以用来select/poll/epoll参数的最大值.
另外,添加事件分为两个内容,首先会调用底层的add回调,它有可能是epoll_ctl(),然后会将该event添加到注册事件的链表中

intevent_add(struct event *ev, const struct timeval *tv){    struct event_base *base = ev->ev_base;//event_base    const struct eventop *evsel = base->evsel;//evsel为底层的操作    void *evbase = base->evbase;    int res = 0;    //为timer事件分配空间,min_heap_reserve的操作实际上是realloc    if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) {        if (min_heap_reserve(&base->timeheap,            1 + min_heap_size(&base->timeheap)) == -1)            return (-1);  /* ENOMEM == errno */    }//reserve    //添加事件    if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) &&        !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {        res = evsel->add(evbase, ev);//底层回调函数        if (res != -1)            event_queue_insert(base, ev, EVLIST_INSERTED);//event插入队列    }    /*      * we should change the timout state only if the previous event     * addition succeeded.     */    if (res != -1 && tv != NULL) {//当上一步操作成功时,如果是timer事件将调整timer堆        struct timeval now;        /*          * we already reserved memory above for the case where we         * are not replacing an exisiting timeout.         */         //表明event已经在timer堆中了,删除旧的        if (ev->ev_flags & EVLIST_TIMEOUT)            event_queue_remove(base, ev, EVLIST_TIMEOUT);        /* Check if it is active due to a timeout.  Rescheduling         * this timeout before the callback can be executed         * removes it from the active list. */        if ((ev->ev_flags & EVLIST_ACTIVE) &&            (ev->ev_res & EV_TIMEOUT)) {            /* See if we are just active executing this             * event in a loop             */            if (ev->ev_ncalls && ev->ev_pncalls) {                /* Abort loop */                *ev->ev_pncalls = 0;            }            event_queue_remove(base, ev, EVLIST_ACTIVE);        }        gettime(base, &now);//计算时间并插入到timer的小根堆中        evutil_timeradd(&now, tv, &ev->ev_timeout);//宏定义:timeout= now + tv        event_queue_insert(base, ev, EVLIST_TIMEOUT);    }    return (res);}

epoll_add函数

上文提到,相同的接口会调用特定的回调,以epoll为例,将调用封装的epoll_add

static intepoll_add(void *arg, struct event *ev){    struct epollop *epollop = arg;    struct epoll_event epev = {0, {0}};    struct evepoll *evep;    int fd, op, events;    if (ev->ev_events & EV_SIGNAL)        return (evsignal_add(ev));    fd = ev->ev_fd;    if (fd >= epollop->nfds) {        /* Extent the file descriptor array as necessary */        if (epoll_recalc(ev->ev_base, epollop, fd) == -1)            return (-1);    }    evep = &epollop->fds[fd];    op = EPOLL_CTL_ADD;    events = 0;    if (evep->evread != NULL) {        events |= EPOLLIN;        op = EPOLL_CTL_MOD;    }    if (evep->evwrite != NULL) {        events |= EPOLLOUT;        op = EPOLL_CTL_MOD;    }    if (ev->ev_events & EV_READ)        events |= EPOLLIN;    if (ev->ev_events & EV_WRITE)        events |= EPOLLOUT;    epev.data.fd = fd;    epev.events = events;    if (epoll_ctl(epollop->epfd, op, ev->ev_fd, &epev) == -1)            return (-1);    /* Update events responsible */    if (ev->ev_events & EV_READ)        evep->evread = ev;    if (ev->ev_events & EV_WRITE)        evep->evwrite = ev;    return (0);}

首先来看传进来的两个参数,在event_add中有如下调用evsel->add(evbase,ev),这个时候就明白evbase的作用了,在event_base_new调用中,
base->evbase=base->evsel->init(base),回调函数将最终调用epoll_init创建一个struct epollop结构体,并返回指针,为什么要这么做?
本质上来说,还是为了可移植性,对于select也会有struct selectop这样的结构体

再看epoll_add函数,将封装的struct eventstruct epollop结构体,转化为epoll认识的struct epoll_event

1 0
原创粉丝点击