之前了解过了多种事件, 但都是从是什么, 如何理解, 如何存储的方面讲解的, 这里就集中的谈谈事件的激活
了解的事件有, 基本IO事件, 信号事件, 超时事件(只是事件可以是单纯的超时事件, 也可以是其他事件带有定时器, 因此不会特意看超时事件)
以及不同优先级事件的处理
虽然IO事件与信号事件是截然不同的处理方式, 但他们的事件处理程序都是处测在同一个event_base中的, 所以想要了解如何处理这两种事件, 关注的点就是event_base的dispatch, 之后蔓延开来
无论是 event_base_dispatch() 还是 event_loop() 都是调用的event_base_loop()
所以, 但凡遇到函数了, 在文章后面部分都会对函数进行分析, 阅读方式是先对event_base_loop函数有个粗略的了解, 之后从头开始, 分析其中详细内容
分析之前, 我自身对其中几个地方是存在疑惑的, 比如线程,对于线程锁、条件变量的设计经常会卡住, 甚至一直搞不明白, 所以遇到锁,条件变量就跳过, 当作是单线程来看把
大纲:
了解的事件有, 基本IO事件, 信号事件, 超时事件(只是事件可以是单纯的超时事件, 也可以是其他事件带有定时器, 因此不会特意看超时事件)
以及不同优先级事件的处理
虽然IO事件与信号事件是截然不同的处理方式, 但他们的事件处理程序都是处测在同一个event_base中的, 所以想要了解如何处理这两种事件, 关注的点就是event_base的dispatch, 之后蔓延开来
无论是 event_base_dispatch() 还是 event_loop() 都是调用的event_base_loop()
注意:
所以, 但凡遇到函数了, 在文章后面部分都会对函数进行分析, 阅读方式是先对event_base_loop函数有个粗略的了解, 之后从头开始, 分析其中详细内容
分析之前, 我自身对其中几个地方是存在疑惑的, 比如线程,对于线程锁、条件变量的设计经常会卡住, 甚至一直搞不明白, 所以遇到锁,条件变量就跳过, 当作是单线程来看把
大纲:
//在event.c中, 这里面省略了些许不相关代码
int
event_base_loop(struct event_base *base, int flags)
{
/* Grab the lock. We will release it inside evsel.dispatch, and again
* as we invoke user callbacks. */
//获取锁, 且是在调用evsel->dispatch() 前释放的锁, 需要配合evsel->dispatch的多线程逻辑
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
//一个base在一个时间段内只能被一个线程运行
if (base->running_loop) {
... ...(error)
}
//如果在event_base_dispatch之前, 有对信号的event_add调用, 那么 base->sig.ev_signal_added就会为1
//反之, 如果用户没有要求关注任何信号, 那么这个if就会被跳过了
//这个函数的目的在于为libevent的信号模块设置全局变量, 比如指定接收信号的base是哪个, 要监听几个信号, IO复用中信号事件的管道一端是哪个
if (base->sig.ev_signal_added && base->sig.ev_n_signals_added)
evsig_set_base(base);
done = 0;
base->event_gotterm = base->event_break = 0;
while (!done) {
base->event_continue = 0;
/* Terminate the loop if we have been asked to */
if (base->event_gotterm) {
break;
}
if (base->event_break) {
break;
}
timeout_correct(base, &tv);
tv_p = &tv;
//如果此时没有已经激活的事件需要处理(如果对这里有疑惑, 那么还请继续往下看, 先假设是第一次启动base), 并且base不是非阻塞的, 此时就要为base指定一个超时时间
//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 */
//没有事件, 且没有激活事件,说明没有事情要base做,即可退出
if (!event_haveevents(base) && !N_ACTIVE_CALLBACKS(base)) {
event_debug(("%s: no events registered.", __func__));
retval = 1;
goto done;
}
/* update last old time */
gettime(base, &base->event_tv);
clear_time_cache(base);
//这就是核心的IO复用函数调用, 就涉及我们要讨论的处理激活事件的过程
//所以看到这里, 我希望可以转到evsel->dispatch函数中去了解其中的内容, 接着继续回到这里
//概括性的来说, evsel->dispatch的目的是在得知有数据要来后, 将对应的文件描述符激活
res = evsel->dispatch(base, tv_p);
if (res == -1) {
... ...(error)
goto done;
}
upd