}
//更新last wait time,并清空time cache
gettime(base, &base->event_tv);
base->tv_cache.tv_sec = 0;
//调用系统IO demultiplexer等待就绪IO events,可能是epoll_wait,或者select等
//在evsel->dispatch()中,会把就绪signal event / IO event插入到激活链表中
res = evsel->dispatch(base, evbase, tv_p);
if(res == -1)
return -1;
//将time cache 赋值为当前系统时间
gettime(base, &base->tv_cache);
//检查heap中的timer events,将就绪的timer event从heap上删除,并插入到激活链表中
timeout_process(base);
//调用event_process_active()处理激活链表中的就绪event,调用其回调函数执行事件处理
//该函数会寻找最高优先级(priority值越小优先级越高)的激活事件链表
//然后处理链表中的所有就绪事件
//因此低优先级的就绪事件可能得不得及时处理
if(base->event_count_active){
event_process_active(base);
if(!base->event_count_active && (flags & EVLOOP_ONCE))
done = 1;
}
else if(flags & EVLOOP_NONBLOCK)
done = 1;
}
//循环结束,清空时间缓存
base->tv_cache.tv_sec = 0;
event_debug("%s: asked to terminate loop.", __func__);
return 0;
}
统一事件源,libevent将timer和signal事件都统一到了系统的IO demultiplex机制中
- timer和IO事件的统一。因为系统的IO机制,例如select()和epoll_wait()都允许程序制定一个最大的等待时间,根据所有timer事件中的最小超时时间来设置IO demultiplex的最大等待时间,当IO返回时,再激活所有就绪的timer事件就可以了,这样就将timer事件完美融合到了系统的IO机制中了。
- IO和signal的统一。因为signal的出现对进程来说是完全随机的。所以当signal发生时,并不立即调用event的callback函数处理信号,而是设法通知系统的IO机制,让其返回,然后再统一和IO事件,以及timer一起处理。
- 通过socketpair来实现的。即一个socket对,其中有两个socket,一个读,一个写。
- 将读socket在事件主循环实例中注册一个读事件,当信号发生时,往写socket中写入一个字符,通常为信号值,此时读socket上有读事件,触发IO demultiplex读事件,然后同普通的IO事件一起被处理即可。
事件主循环的流程如下 1) 开始
2) 调整系统时间与否
3) 根据timer heap中的event的最小超时时间计算系统IO demultiplexer的最大等待时间
4) 更新last wait time, 并清空time cache
5) 调用系统I/O demultiplexer等待就绪I/O events
6) 检查signal的激活标记,如果被设置,则检查激活signal event,并将event插入到激活链表中
7) 将就绪的I/O event插入到激活链表中
8) 检查heap中的timer events,将就绪的timer event从heap上删除,并插入到激活链表中
9) 根据优先级处理激活链表中的就绪event,调用其回调函数执行事件处理(优先级越小越高)
10) 结束
总结
- 本文只是从libevent中的事件循环主框架上面做了一个简单的介绍。
- 若是想要了解libenvet更多的具体实现细节,请参考网络
- 本文参考了libevent源码深度剖析 和《linux高性能服务器编程》
|