libevent代码阅读(13)——epoll的事件等待以及分发过程

来源:互联网 发布:java object转换date 编辑:程序博客网 时间:2024/06/08 06:28
在前面的章节我们提到event_base_loop中会调用具体的io复用机制的事件等待以及分发函数evsel->dispatch,然后调用event_process_active处理已经激活的事件。

对于epoll而言,dispatch就是epoll_dispatch,我们看一下它的执行流程

// 事件分发static intepoll_dispatch(struct event_base *base, struct timeval *tv){struct epollop *epollop = base->evbase;struct epoll_event *events = epollop->events;int i, res;long timeout = -1;if (tv != NULL) {timeout = evutil_tv_to_msec(tv);if (timeout < 0 || timeout > MAX_EPOLL_TIMEOUT_MSEC) {/* Linux kernels can wait forever if the timeout is * too big; see comment on MAX_EPOLL_TIMEOUT_MSEC. */timeout = MAX_EPOLL_TIMEOUT_MSEC;}}// 暂时不用管下面两个函数的调用epoll_apply_changes(base);event_changelist_remove_all(&base->changelist, base);EVBASE_RELEASE_LOCK(base, th_base_lock);// 等待事件发生,res存放事件发生的个数res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout);EVBASE_ACQUIRE_LOCK(base, th_base_lock);// 出错if (res == -1) {if (errno != EINTR) {event_warn("epoll_wait");return (-1);}return (0);}event_debug(("%s: epoll_wait reports %d", __func__, res));EVUTIL_ASSERT(res <= epollop->nevents);// 遍历所有触发的事件for (i = 0; i < res; i++){int what = events[i].events;short ev = 0;if (what & (EPOLLHUP|EPOLLERR)){// 有错误发生ev = EV_READ | EV_WRITE;}else{// 可读if (what & EPOLLIN)ev |= EV_READ;// 可写if (what & EPOLLOUT)ev |= EV_WRITE;}if (!ev)continue;// 激活io事件映射表中的事件处理器evmap_io_active(base, events[i].data.fd, ev | EV_ET);}// 如果所有的事件都被触发了,表示事件数组的大小还是太小了,需要扩展事件数组的大小if (res == epollop->nevents && epollop->nevents < MAX_NEVENT) {/* We used all of the event space this time.  We should   be ready for more events next time. */int new_nevents = epollop->nevents * 2;struct epoll_event *new_events;new_events = mm_realloc(epollop->events,    new_nevents * sizeof(struct epoll_event));if (new_events) {epollop->events = new_events;epollop->nevents = new_nevents;}}return (0);}

我们看到epoll_dispatch中调用了一个evmap_io_active函数,这个函数会将激活的事件插入到已激活的事件列表中

/* * 激活io事件映射表中的事件,然后插入到已激活事件队列中 */voidevmap_io_active(struct event_base *base, evutil_socket_t fd, short events){struct event_io_map *io = &base->io;struct evmap_io *ctx;struct event *ev;#ifndef EVMAP_USE_HTEVUTIL_ASSERT(fd < io->nentries);#endifGET_IO_SLOT(ctx, io, fd, evmap_io);EVUTIL_ASSERT(ctx);TAILQ_FOREACH(ev, &ctx->events, ev_io_next) {if (ev->ev_events & events)event_active_nolock(ev, ev->ev_events & events, 1);}}
event_active_nolock函数的过程如下:

/* * 非阻塞的激活事件,并将它存放于己激活事件列表中 */voidevent_active_nolock(struct event *ev, int res, short ncalls){struct event_base *base;event_debug(("event_active: %p (fd "EV_SOCK_FMT"), res %d, callback %p",ev, EV_SOCK_ARG(ev->ev_fd), (int)res, ev->ev_callback));/* We get different kinds of events, add them together */// 如果本事件已经存在于已激活事件列表中,那么就返回if (ev->ev_flags & EVLIST_ACTIVE) {ev->ev_res |= res;return;}base = ev->ev_base;EVENT_BASE_ASSERT_LOCKED(base);ev->ev_res = res;// 如果当前事件的优先级小于event_base的运行时优先级// 就让event_base跳过本次循环,进行下一次循环if (ev->ev_pri < base->event_running_priority)base->event_continue = 1;// 如果是信号事件if (ev->ev_events & EV_SIGNAL) {#ifndef _EVENT_DISABLE_THREAD_SUPPORTif (base->current_event == ev && !EVBASE_IN_THREAD(base)) {++base->current_event_waiters;EVTHREAD_COND_WAIT(base->current_event_cond, base->th_base_lock);}#endifev->ev_ncalls = ncalls;ev->ev_pncalls = NULL;}// 把被激活的事件插入到已激活事件列表event_queue_insert(base, ev, EVLIST_ACTIVE);if (EVBASE_NEED_NOTIFY(base))evthread_notify_base(base);}

event_queue_insert函数才正真将事件插入到已激活事件队列中:

/* * 将事件处理器添加到各种事件队列中 * 将io事件处理器和信号事件处理器插入注册事件队列 * 将定时器插入通用定时器队列或时间堆 * 将被激活的事件处理器添加到活动事件队列中 */static voidevent_queue_insert(struct event_base *base, struct event *ev, int queue){EVENT_BASE_ASSERT_LOCKED(base);// 避免重复插入if (ev->ev_flags & queue) {/* Double insertion is possible for active events */if (queue & EVLIST_ACTIVE)return;event_errx(1, "%s: %p(fd "EV_SOCK_FMT") already on queue %x", __func__,ev, EV_SOCK_ARG(ev->ev_fd), queue);return;}// 增加event_base拥有的事件处理器的数量if (~ev->ev_flags & EVLIST_INTERNAL)base->event_count++;// 标记该事件处理器已经被处理过ev->ev_flags |= queue;// 根据不同的事件插入到不同的队列中switch (queue) {// 将io事件处理器或信号事件处理器插入注册事件队列case EVLIST_INSERTED:TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next);break;// 将就绪事件处理器插入活动事件队列case EVLIST_ACTIVE:base->event_count_active++;TAILQ_INSERT_TAIL(&base->activequeues[ev->ev_pri],ev,ev_active_next);break;// 将定时器插入到通用定时器队列或时间堆case EVLIST_TIMEOUT: {if (is_common_timeout(&ev->ev_timeout, base)) {struct common_timeout_list *ctl =get_common_timeout_list(base, &ev->ev_timeout);insert_common_timeout_inorder(ctl, ev);} elsemin_heap_push(&base->timeheap, ev);break;}default:event_errx(1, "%s: unknown queue %x", __func__, queue);}}



0 0
原创粉丝点击