Home Page
Search
\begin{md} ## 代码 ``` c #include
#include
#include
#include
#include
void sig_cb(int fd, short events, void *arg) { printf("%s\n", __func__); } int main() { struct event_base *base = event_base_new(); struct event *ev = evsignal_new(base, SIGINT, sig_cb, NULL); event_add(ev, NULL); printf("pid: %d\n", getpid()); event_base_dispatch(base); return 0; } ``` 本篇文章来分析上面信号监听代码的完整执行流程。使用的 libevent 版本为 **release-2.1.11-stable**。 ## 流程分析 首先调用 `event_base_new()` 函数返回一个 `event_base` 结构体指针。 这里就出现了两个问题: 1. `event_base` 结构体的定义是什么?功能是什么? 2. `event_base_new()` 函数的功能是什么? ### event_base 结构体 `event_base` 结构体的定义如下: ``` c struct event_base { /** Function pointers and other data to describe this event_base's * backend. */ const struct eventop *evsel; /** Pointer to backend-specific data. */ void *evbase; /** List of changes to tell backend about at next dispatch. Only used * by the O(1) backends. */ // 在下一次调度时告知后端的变更列表 struct event_changelist changelist; /** Function pointers used to describe the backend that this event_base * uses for signals */ const struct eventop *evsigsel; /** Data to implement the common signal handler code. */ struct evsig_info sig; /** Number of virtual events */ int virtual_event_count; /** Maximum number of virtual events active */ int virtual_event_count_max; /** Number of total events added to this event_base */ int event_count; /** Maximum number of total events added to this event_base */ int event_count_max; /** Number of total events active in this event_base */ int event_count_active; /** Maximum number of total events active in this event_base */ int event_count_active_max; /** Set if we should terminate the loop once we're done processing * events. */ int event_gotterm; /** Set if we should terminate the loop immediately */ int event_break; /** Set if we should start a new instance of the loop immediately. */ int event_continue; /** The currently running priority of events */ int event_running_priority; /** Set if we're running the event_base_loop function, to prevent * reentrant invocation. */ int running_loop; /** Set to the number of deferred_cbs we've made 'active' in the * loop. This is a hack to prevent starvation; it would be smarter * to just use event_config_set_max_dispatch_interval's max_callbacks * feature */ int n_deferreds_queued; /* Active event management. */ /** An array of nactivequeues queues for active event_callbacks (ones * that have triggered, and whose callbacks need to be called). Low * priority numbers are more important, and stall higher ones. */ struct evcallback_list *activequeues; /** The length of the activequeues array */ int nactivequeues; /** A list of event_callbacks that should become active the next time * we process events, but not this time. */ // event_callbacks 的列表,下次(todo:下个循环的意思吧?)我们处理事件时应该激活,但这次不会激活 struct evcallback_list active_later_queue; /* common timeout logic */ /** An array of common_timeout_list* for all of the common timeout * values we know. */ struct common_timeout_list **common_timeout_queues; /** The number of entries used in common_timeout_queues */ int n_common_timeouts; /** The total size of common_timeout_queues. */ int n_common_timeouts_allocated; /** Mapping from file descriptors to enabled (added) events */ struct event_io_map io; /** Mapping from signal numbers to enabled (added) events. */ struct event_signal_map sigmap; /** Priority queue of events with timeouts. */ struct min_heap timeheap; /** Stored timeval: used to avoid calling gettimeofday/clock_gettime * too often. */ struct timeval tv_cache; struct evutil_monotonic_timer monotonic_timer; /** Difference between internal time (maybe from clock_gettime) and * gettimeofday. */ struct timeval tv_clock_diff; /** Second in which we last updated tv_clock_diff, in monotonic time. */ time_t last_updated_clock_diff; #ifndef EVENT__DISABLE_THREAD_SUPPORT /* threading support */ /** The thread currently running the event_loop for this base */ unsigned long th_owner_id; /** A lock to prevent conflicting accesses to this event_base */ void *th_base_lock; /** A condition that gets signalled when we're done processing an * event with waiters on it. */ void *current_event_cond; /** Number of threads blocking on current_event_cond. */ int current_event_waiters; #endif /** The event whose callback is executing right now */ struct event_callback *current_event; #ifdef _WIN32 /** IOCP support structure, if IOCP is enabled. */ struct event_iocp_port *iocp; #endif /** Flags that this base was configured with */ enum event_base_config_flag flags; struct timeval max_dispatch_time; int max_dispatch_callbacks; int limit_callbacks_after_prio; /* Notify main thread to wake up break, etc. */ /** True if the base already has a pending notify, and we don't need * to add any more. */ int is_notify_pending; /** A socketpair used by some th_notify functions to wake up the main * thread. */ evutil_socket_t th_notify_fd[2]; /** An event used by some th_notify functions to wake up the main * thread. */ struct event th_notify; /** A function used to wake up the main thread from another thread. */ int (*th_notify_fn)(struct event_base *base); /** Saved seed for weak random number generator. Some backends use * this to produce fairness among sockets. Protected by th_base_lock. */ struct evutil_weakrand_state weakrand_seed; /** List of event_onces that have not yet fired. */ LIST_HEAD(once_event_list, event_once) once_events; }; ``` todo: 详细说明 event_base 的功能、生命周期 ### event_base_new() 函数 下面是 `event_base_new()` 函数的定义: ``` c struct event_base * event_base_new(void) { struct event_base *base = NULL; struct event_config *cfg = event_config_new(); if (cfg) { base = event_base_new_with_config(cfg); event_config_free(cfg); } return base; } ``` `event_base_new()` 函数主要做了两件事: 1. 调用 `event_config_new()` 函数创建一个 `event_config` 结构体并返回指针。 2. 调用 `event_base_new_with_config()` 函数创建一个 `event_base` 结构体并返回指针。 #### event_config_new() 先看一下 ` `event_config` ` 结构体的定义 ``` c /** Internal structure: describes the configuration we want for an event_base * that we're about to allocate. */ struct event_config { //定义一个头结点结构体,结构体名为event_configq,结点类型为event_config_entry,头结点变量名为entries TAILQ_HEAD(event_configq, event_config_entry) entries; int n_cpus_hint; // 保存 CPU 的核心数 struct timeval max_dispatch_interval; int max_dispatch_callbacks; int limit_callbacks_after_prio; enum event_method_feature require_features; //要求满足的特征:ET、O1、FDS enum event_base_config_flag flags; //其他配置要求:无锁、不检查环境变量、... }; ``` **这是一个 libevent 内部使用的 struct, 用来保存我们希望给 event_base 分配的属性。** 再看一下 `event_config_new()` 这个函数是如何创建一个 `event_config` 结构体的。 ``` c struct event_config * event_config_new(void) { struct event_config *cfg = mm_calloc(1, sizeof(*cfg)); if (cfg == NULL) return (NULL); TAILQ_INIT(&cfg->entries); // 初始化 cfg->entries cfg->max_dispatch_interval.tv_sec = -1; // todo cfg->max_dispatch_callbacks = INT_MAX; // todo cfg->limit_callbacks_after_prio = 1; // todo return (cfg); } ``` #### event_base_new_with_config() 从函数名可以看出,该函数的功能就是根据给定的 `event_config` 结构体来创建一个合乎要求的 `event_base` 结构体。 ``` c struct event_base * event_base_new_with_config(const struct event_config *cfg) { int i; struct event_base *base; int should_check_environment; #ifndef EVENT__DISABLE_DEBUG_MODE event_debug_mode_too_late = 1; #endif // 申请一个 event_base if ((base = mm_calloc(1, sizeof(struct event_base))) == NULL) { event_warn("%s: calloc", __func__); return NULL; } // 将 cfg 的 flag 参数复制给 base if (cfg) base->flags = cfg->flags; // 如果 cfg 的 flag 设置为了忽略环境变量的话 should_check_environment 设置为 0 should_check_environment = !(cfg && (cfg->flags & EVENT_BASE_FLAG_IGNORE_ENV)); { struct timeval tmp; // todo 精确时间 int precise_time = cfg && (cfg->flags & EVENT_BASE_FLAG_PRECISE_TIMER); int flags; if (should_check_environment && !precise_time) { precise_time = evutil_getenv_("EVENT_PRECISE_TIMER") != NULL; // todo 上面的 base->flags = cfg->flags 这句不是已经保证了 base 和 cfg 的 flag 属性相同吗?下面这个 if 句子好像没有作用 if (precise_time) { base->flags |= EVENT_BASE_FLAG_PRECISE_TIMER; } } flags = precise_time ? EV_MONOT_PRECISE : 0; // 配置单调时钟 todo: 具体作用是啥 evutil_configure_monotonic_time_(&base->monotonic_timer, flags); gettime(base, &tmp); } // 对时间堆进行初始化 min_heap_ctor_(&base->timeheap); // 对 base 的部分参数进行初始化 base->sig.ev_signal_pair[0] = -1; base->sig.ev_signal_pair[1] = -1; base->th_notify_fd[0] = -1; base->th_notify_fd[1] = -1; TAILQ_INIT(&base->active_later_queue); evmap_io_initmap_(&base->io); evmap_signal_initmap_(&base->sigmap); event_changelist_init_(&base->changelist); base->evbase = NULL; // 设置 base 的最大调度时间和 limit_callbacks_after_prio(todo: 不知道这个是啥) if (cfg) { memcpy(&base->max_dispatch_time, &cfg->max_dispatch_interval, sizeof(struct timeval)); base->limit_callbacks_after_prio = cfg->limit_callbacks_after_prio; } else { base->max_dispatch_time.tv_sec = -1; base->limit_callbacks_after_prio = 1; } if (cfg && cfg->max_dispatch_callbacks >= 0) { base->max_dispatch_callbacks = cfg->max_dispatch_callbacks; } else { base->max_dispatch_callbacks = INT_MAX; } if (base->max_dispatch_callbacks == INT_MAX && base->max_dispatch_time.tv_sec == -1) base->limit_callbacks_after_prio = INT_MAX; // eventops 数组是系统中拥有的有后端信息,依次遍历,直到找到一个符合要求的后端给 base->evsel for (i = 0; eventops[i] && !base->evbase; i++) { if (cfg != NULL) { /* determine if this backend should be avoided */ if (event_config_is_avoided_method(cfg, eventops[i]->name)) continue; if ((eventops[i]->features & cfg->require_features) != cfg->require_features) continue; } /* also obey the environment variables */ if (should_check_environment && event_is_method_disabled(eventops[i]->name)) continue; base->evsel = eventops[i]; base->evbase = base->evsel->init(base); } if (base->evbase == NULL) { event_warnx("%s: no event mechanism available", __func__); base->evsel = NULL; event_base_free(base); return NULL; } if (evutil_getenv_("EVENT_SHOW_METHOD")) event_msgx("libevent using: %s", base->evsel->name); /* allocate a single active event queue */ if (event_base_priority_init(base, 1) < 0) { event_base_free(base); return NULL; } /* prepare for threading */ #if !defined(EVENT__DISABLE_THREAD_SUPPORT) && !defined(EVENT__DISABLE_DEBUG_MODE) // 这个变量用来检测在多线程中是否乱序执行 event_debug_created_threadable_ctx_ = 1; #endif // 省略部分代码 return (base); } ``` 到这个地方我们完全了解了 `struct event_base *base = event_base_new();` 这行代码的来龙去脉,接下来看第二句代码 `struct event *ev = evsignal_new(base, SIGINT, sig_cb, NULL);` 。在这行代码中,可以提出两个问题: 1. `event` 结构体是什么?它的功能是什么? 2. `evsignal_new()` 函数的功能是什么? ### event 结构体 ``` c struct event { struct event_callback ev_evcallback; /* for managing timeouts */ union { TAILQ_ENTRY(event) ev_next_with_common_timeout; int min_heap_idx; } ev_timeout_pos; evutil_socket_t ev_fd; struct event_base *ev_base; union { /* used for io events */ struct { LIST_ENTRY (event) ev_io_next; struct timeval ev_timeout; } ev_io; /* used by signal events */ struct { LIST_ENTRY (event) ev_signal_next; short ev_ncalls; /* Allows deletes in callback */ short *ev_pncalls; } ev_signal; } ev_; short ev_events; short ev_res; /* result passed to event callback */ struct timeval ev_timeout; }; ``` ### evsignal_new() 函数 从下面的宏定义中可以看到, `evsignal_new()` 函数其实就是 `event_new()` 函数。 ``` c // 要注意到这里的第三个参数 EV_SIGNAL|EV_PERSIST 表明了这是一个信号事件 #define evsignal_new(b, x, cb, arg) \ event_new((b), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg)) /** Allocate and assign a new event structure, ready to be added. The function event_new() returns a new event that can be used in future calls to event_add() and event_del(). The fd and events arguments determine which conditions will trigger the event; the callback and callback_arg arguments tell Libevent what to do when the event becomes active. If events contains one of EV_READ, EV_WRITE, or EV_READ|EV_WRITE, then fd is a file descriptor or socket that should get monitored for readiness to read, readiness to write, or readiness for either operation (respectively). If events contains EV_SIGNAL, then fd is a signal number to wait for. If events contains none of those flags, then the event can be triggered only by a timeout or by manual activation with event_active(): In this case, fd must be -1. The EV_PERSIST flag can also be passed in the events argument: it makes event_add() persistent until event_del() is called. The EV_ET flag is compatible with EV_READ and EV_WRITE, and supported only by certain backends. It tells Libevent to use edge-triggered events. The EV_TIMEOUT flag has no effect here. It is okay to have multiple events all listening on the same fds; but they must either all be edge-triggered, or all not be edge triggered. When the event becomes active, the event loop will run the provided callback function, with three arguments. The first will be the provided fd value. The second will be a bitfield of the events that triggered: EV_READ, EV_WRITE, or EV_SIGNAL. Here the EV_TIMEOUT flag indicates that a timeout occurred, and EV_ET indicates that an edge-triggered event occurred. The third event will be the callback_arg pointer that you provide. @param base the event base to which the event should be attached. @param fd the file descriptor or signal to be monitored, or -1. @param events desired events to monitor: bitfield of EV_READ, EV_WRITE, EV_SIGNAL, EV_PERSIST, EV_ET. @param callback callback function to be invoked when the event occurs @param callback_arg an argument to be passed to the callback function @return a newly allocated struct event that must later be freed with event_free() or NULL if an error occurred. @see event_free(), event_add(), event_del(), event_assign() */ struct event * event_new(struct event_base *base, evutil_socket_t fd, short events, void (*cb)(evutil_socket_t, short, void *), void *arg) { struct event *ev; ev = mm_malloc(sizeof(struct event)); if (ev == NULL) return (NULL); if (event_assign(ev, base, fd, events, cb, arg) < 0) { mm_free(ev); return (NULL); } return (ev); } ``` 函数的描述已经将函数的功能描述的很好了,可以仔细看看。可以看到 `event_new()` 函数内部调用了一个 `event_assign()` 函数,这个函数才是重点。 ``` c int event_assign(struct event *ev, struct event_base *base, evutil_socket_t fd, short events, void (*callback)(evutil_socket_t, short, void *), void *arg) { // current_base 是 event_global_current_base_ 变量的 #define, 是一个 event_base if (!base) base = current_base; if (arg == &event_self_cbarg_ptr_) arg = ev; // 如果事件不是信号事件的话要确保 fd 不是阻塞的 if (!(events & EV_SIGNAL)) event_debug_assert_socket_nonblocking_(fd); //确保 event 没有被添加到 event_base 中 event_debug_assert_not_added_(ev); // 配置 ev 的属性 ev->ev_base = base; ev->ev_callback = callback; ev->ev_arg = arg; ev->ev_fd = fd; ev->ev_events = events; ev->ev_res = 0; ev->ev_flags = EVLIST_INIT; ev->ev_ncalls = 0; ev->ev_pncalls = NULL; // 如果还记得前面提到的那个 evsignal_new 宏定义的话就应该知道这一定是一个信号事件,所以这个 if 肯定是成立的 if (events & EV_SIGNAL) { if ((events & (EV_READ|EV_WRITE|EV_CLOSED)) != 0) { event_warnx("%s: EV_SIGNAL is not compatible with " "EV_READ, EV_WRITE or EV_CLOSED", __func__); return -1; } ev->ev_closure = EV_CLOSURE_EVENT_SIGNAL; } else { if (events & EV_PERSIST) { evutil_timerclear(&ev->ev_io_timeout); ev->ev_closure = EV_CLOSURE_EVENT_PERSIST; } else { ev->ev_closure = EV_CLOSURE_EVENT; } } min_heap_elem_init_(ev); if (base != NULL) { /* by default, we put new events into the middle priority */ // 默认将 新 events 的优先级设置为激活队列长度的一半 ev->ev_pri = base->nactivequeues / 2; } /* record that ev is now setup (that is, ready for an add) */ // 记录这个 ev 已经设置好了,可以进行添加了 event_debug_note_setup_(ev); return 0; } ``` 现在第二行代码 `struct event *ev = evsignal_new(base, SIGINT, sig_cb, NULL);` 已经分析完了。开始分析第三行 `event_add(ev, NULL);` 。 ### event_add() 函数 ``` c int event_add(struct event *ev, const struct timeval *tv) { int res; // 检查一下 event 的 event_base 是否设置了 if (EVUTIL_FAILURE_CHECK(!ev->ev_base)) { event_warnx("%s: event has no event_base set.", __func__); return -1; } EVBASE_ACQUIRE_LOCK(ev->ev_base, th_base_lock); res = event_add_nolock_(ev, tv, 0); EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock); return (res); } ``` 可以发现,这个函数的重点在于 `event_add_nolock_()` 函数。 ``` c /* Implementation function to add an event. Works just like event_add, * except: 1) it requires that we have the lock. 2) if tv_is_absolute is set, * we treat tv as an absolute time, not as an interval to add to the current * time */ int event_add_nolock_(struct event *ev, const struct timeval *tv, int tv_is_absolute) { struct event_base *base = ev->ev_base; int res = 0; int notify = 0; EVENT_BASE_ASSERT_LOCKED(base); event_debug_assert_is_setup_(ev); event_debug(( "event_add: event: %p (fd "EV_SOCK_FMT"), %s%s%s%scall %p", ev, EV_SOCK_ARG(ev->ev_fd), ev->ev_events & EV_READ ? "EV_READ " : " ", ev->ev_events & EV_WRITE ? "EV_WRITE " : " ", ev->ev_events & EV_CLOSED ? "EV_CLOSED " : " ", tv ? "EV_TIMEOUT " : " ", ev->ev_callback)); // todo ~EVLIST_ALL 不是 0 吗? 那么这个测试肯定为真,不知道有什么意思 EVUTIL_ASSERT(!(ev->ev_flags & ~EVLIST_ALL)); if (ev->ev_flags & EVLIST_FINALIZING) { /* XXXX debug */ return (-1); } /* * prepare for timeout insertion further below, if we get a * failure on any step, we should not change any state. */ //如果event设置了超时,并且event所设超时结构体不在time小根堆上,则在time小根堆中预留空间 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 */ } /* If the main thread is currently executing a signal event's * callback, and we are not the main thread, then we want to wait * until the callback is done before we mess with the event, or else * we can race on ev_ncalls and ev_pncalls below. */ // 如果支持多线程 #ifndef EVENT__DISABLE_THREAD_SUPPORT // 如果当前线程正在执行一个信号 event 的回调函数,并且我们这条线程不是主线程的话,我们就得等回调函数执行完 if (base->current_event == event_to_event_callback(ev) && (ev->ev_events & EV_SIGNAL) && !EVBASE_IN_THREAD(base)) { ++base->current_event_waiters; // 当前 event cond 上阻塞的线程数加 1 EVTHREAD_COND_WAIT(base->current_event_cond, base->th_base_lock); } #endif // 下面比较好理解,就是根据 event 的类型将其加入到对应的队列中 if ((ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED|EV_SIGNAL)) && !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE|EVLIST_ACTIVE_LATER))) { if (ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED)) res = evmap_io_add_(base, ev->ev_fd, ev); else if (ev->ev_events & EV_SIGNAL) res = evmap_signal_add_(base, (int)ev->ev_fd, ev); if (res != -1) // 如果插入成功,记录一下这个 event 已经插入过 event_queue_insert_inserted(base, ev); if (res == 1) { /* evmap says we need to notify the main thread. */ notify = 1; res = 0; } } /* * we should change the timeout state only if the previous event * addition succeeded. */ if (res != -1 && tv != NULL) { struct timeval now; int common_timeout; #ifdef USE_REINSERT_TIMEOUT int was_common; int old_timeout_idx; #endif /* * for persistent timeout events, we remember the * timeout value and re-add the event. * * If tv_is_absolute, this was already set. */ //如果是相对时间,并且是永久事件,那么就将tv赋值给ev_io.ev_timeout成员,记录下该永久事件的相对超时时长 if (ev->ev_closure == EV_CLOSURE_EVENT_PERSIST && !tv_is_absolute) ev->ev_io_timeout = *tv; #ifndef USE_REINSERT_TIMEOUT if (ev->ev_flags & EVLIST_TIMEOUT) { event_queue_remove_timeout(base, ev); } #endif /* 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)) { if (ev->ev_events & EV_SIGNAL) { /* 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_active(base, event_to_event_callback(ev)); } gettime(base, &now); common_timeout = is_common_timeout(tv, base); #ifdef USE_REINSERT_TIMEOUT was_common = is_common_timeout(&ev->ev_timeout, base); old_timeout_idx = COMMON_TIMEOUT_IDX(&ev->ev_timeout); #endif if (tv_is_absolute) { ev->ev_timeout = *tv; } else if (common_timeout) { // 相对时间转绝对时间 struct timeval tmp = *tv; tmp.tv_usec &= MICROSECONDS_MASK; evutil_timeradd(&now, &tmp, &ev->ev_timeout); ev->ev_timeout.tv_usec |= (tv->tv_usec & ~MICROSECONDS_MASK); } else { evutil_timeradd(&now, tv, &ev->ev_timeout); } event_debug(( "event_add: event %p, timeout in %d seconds %d useconds, call %p", ev, (int)tv->tv_sec, (int)tv->tv_usec, ev->ev_callback)); #ifdef USE_REINSERT_TIMEOUT event_queue_reinsert_timeout(base, ev, was_common, common_timeout, old_timeout_idx); #else event_queue_insert_timeout(base, ev); #endif if (common_timeout) { struct common_timeout_list *ctl = get_common_timeout_list(base, &ev->ev_timeout); if (ev == TAILQ_FIRST(&ctl->events)) { common_timeout_schedule(ctl, &now, ev); } } else { struct event* top = NULL; /* See if the earliest timeout is now earlier than it * was before: if so, we will need to tell the main * thread to wake up earlier than it would otherwise. * We double check the timeout of the top element to * handle time distortions due to system suspension. */ if (min_heap_elt_is_top_(ev)) notify = 1; else if ((top = min_heap_top_(&base->timeheap)) != NULL && evutil_timercmp(&top->ev_timeout, &now, <)) notify = 1; } } /* if we are not in the right thread, we need to wake up the loop */ if (res != -1 && notify && EVBASE_NEED_NOTIFY(base)) evthread_notify_base(base); event_debug_note_add_(ev); return (res); } ``` 上面就将 `event_add(ev, NULL);` 这句分析完成了。里面比较重要但是没有提到的一个点是超时队列。下面看最后一句 `event_base_dispatch(base);` 。 ### event_base_dispatch() 函数 ``` c int event_base_dispatch(struct event_base *event_base) { return (event_base_loop(event_base, 0)); } ``` 这个函数的内部其实就是直接返回了 `event_base_loop()` 函数的结果。 ``` c int event_base_loop(struct event_base *base, int flags) { //设置为EVLOOP_NONBLOCK,那么event_loop只会处理当前已经激活的event,处理结束后就会退出event_loop //设置为EVLOOP_ONCE,那么event_loop就会等待到第一个事件超时,处理在这段时间内激活的event,直到所有激活的事件都处理完就退出event_loop //设置为其他值,那么就会一直循环监听,直到被动退出 const struct eventop *evsel = base->evsel; struct timeval tv; struct timeval *tv_p; int res, done, retval = 0; /* Grab the lock. We will release it inside evsel.dispatch, and again * as we invoke user callbacks. */ // event_base 初始化后默认分配的是递归锁 EVBASE_ACQUIRE_LOCK(base, th_base_lock); // 检查一下是不是已经启动过事件循环 if (base->running_loop) { event_warnx("%s: reentrant invocation. Only one event_base_loop" " can run on each event_base at once.", __func__); EVBASE_RELEASE_LOCK(base, th_base_lock); return -1; } // 记录这个 event_base 正在执行时事件循环 base->running_loop = 1; // 清空缓存的超时,todo: 作用是什么? clear_time_cache(base); if (base->sig.ev_signal_added && base->sig.ev_n_signals_added) evsig_set_base_(base); done = 0; #ifndef EVENT__DISABLE_THREAD_SUPPORT // 存放执行 base 主循环的线程 id base->th_owner_id = EVTHREAD_GET_ID(); #endif base->event_gotterm = base->event_break = 0; // 开始 base 循环 while (!done) { base->event_continue = 0; base->n_deferreds_queued = 0; /* Terminate the loop if we have been asked to */ if (base->event_gotterm) { break; } if (base->event_break) { break; } tv_p = &tv; // 如果没有被激活的事件并且是 flags 设置为 EVLOOP_NONBLOCK // 则获取从现在开始到第一个超时事件的时长保存到 tv_p 的首元素中(具体实现还是分几种情况的 // 可以到 timeout_next() 函数里面去看下) if (!N_ACTIVE_CALLBACKS(base) && !(flags & EVLOOP_NONBLOCK)) { timeout_next(base, &tv_p); } else { /* * if we have active events, we just poll new events * without waiting. */ evutil_timerclear(&tv); } /* If we have no events, we just exit */ if (0==(flags&EVLOOP_NO_EXIT_ON_EMPTY) && !event_haveevents(base) && !N_ACTIVE_CALLBACKS(base)) { event_debug(("%s: no events registered.", __func__)); retval = 1; goto done; } // todo: 不知道这个函数在干啥 event_queue_make_later_events_active(base); clear_time_cache(base); // 由具体的后端去执行调度 res = evsel->dispatch(base, tv_p); if (res == -1) { event_debug(("%s: dispatch returned unsuccessfully.", __func__)); retval = -1; goto done; } update_time_cache(base); timeout_process(base); //将base的min_heap中所有超时的事件以超时激活类型添加到激活队列中 if (N_ACTIVE_CALLBACKS(base)) { int n = event_process_active(base); if ((flags & EVLOOP_ONCE) && N_ACTIVE_CALLBACKS(base) == 0 && n != 0) done = 1; } else if (flags & EVLOOP_NONBLOCK) done = 1; } event_debug(("%s: asked to terminate loop.", __func__)); done: clear_time_cache(base); base->running_loop = 0; EVBASE_RELEASE_LOCK(base, th_base_lock); return (retval); } ``` 上面就是最后一行重要代码的内部流程,其实就是开始了一个循环,不断检测是否有关注的事件发生,并根据条件来决定是否退出循环。 ## 思考 这种设计有什么好处?其他同类框架是怎么设计的?这种设计的缺点是什么? ## TODO 1. USE_REINSERT_TIMEOUT 这个宏定义的作用需要研究下。 2. 几类 event 队列需要详细研究下 3. eventop 详细解释 4. 为什么函数中老是要执行 clear_time_cache() ## 参考 [libevent 源码学习(6):事件处理基础——event_base 的创建](https://blog.csdn.net/qq_28114615/article/details/92847048#event_config%E7%BB%93%E6%9E%84%E4%BD%93) [libevent 源码学习(9):事件 event](https://blog.csdn.net/qq_28114615/article/details/96864601) [libevent 源码学习(13):事件主循环 event_base_loop](https://blog.csdn.net/qq_28114615/article/details/96826553) \end{md}
Home Page