读libevent源码学习网络库对定时器和信号的处理

来源:互联网 发布:中石化邮箱域名是什么 编辑:程序博客网 时间:2024/05/29 14:45

读了一下libevent的部分代码,主要是timer,signal,epoll相关的,学习了网络库如何处理定时器事件和信号。

大多数网络模块的库应该都是这样实现的,很巧妙。


首先说timer,libevent通过一个小根堆结构来保存定时事件,堆顶元素是最近即将超时的时间,例如右5个定时器,分别在2S,1S,4S,7S,8S后超时,那么堆顶的元素就是1S的定时器,这有什么用呢?

因为不管是select/poll/epoll,监控文件描述符的时候都会设置一个超时间隔,我们恰好可以把堆顶元素的超时时间作为这个超时间隔。

还是上面的5个timer,在本次的epoll_wait中我们可以取出堆顶元素1S作为超时间隔,分以下两种情况:

1:如果1S内没有I/O事件,那么epoll_wait将在1S后超时。此时应该触发1S的定时事件。

2:如果1S内发生了I/O事件,那么epoll_wait返回时,1S的定时事件不应该触发。

所以,每次epoll_wait返回时,我们就依次取出堆顶元素,如果超时,就处理超时事件。

伪代码:

while (1)  {    // 从timer_heap中计算本次epoll_wait的超时间隔    int timeout = timer_heap->nearest_timeout(now);    int nfds = epoll_wait(fd, events, events_size, timeout);    ....    timer_handler *handler = NULL;    //如果handler != NULL,则处理定时事件,可能会有多个定时器超时,所以用while循环    while ((hander = timer_heap->check_time_out(now)) != NULL)      handler->process_timeout(now);  }


再说说signal事件,这个也是转化为I/O事件,通过socketpair或者pipe都可以,也就是epoll_wait中监控管道的读端,当接受到信号时,信号处理程序通过管道的写端把信号写进去,从而触发I/O事件,读端把信号读出来。


太巧妙了,把定时器事件和signal事件都转化为I/O事件了。并且定时器也可以精确到ms级别。

我自己写了下,定时器还是相当精确的,误差在1ms以内。


原创粉丝点击