libevent源码分析--如何将定时器和信号事件都集合到I/O复用中

时间:2022-04-09 00:16:08

我的理解:

       在I/O多路复用机制用(这里使用epoll作为例子),epoll_wait在一定的时间内监听需要受到关注的套接字,如果只是关注I/O事件,这样就会很简单,在设置时间的时候没有任何限制,此函数返回后,得到的有事件的套接字的个数,在libevent中使用的event_base_loop这个函数打到这个目的的,里面的evse->dispatch()这个函数就是epoll_wait的包装,这个函数返回的有I/O事件的套接字的数目。

      返回以后,再次进入event_base_loop这个函数,在这个函数中调用event_process_active这个函数调用相应套接字的回调函数,做相应的工作,那么在这两者(返回的是个数,操作却能够完成这么多个数的套接字的事件处理)是怎么沟通的呢?其实在evsel->dispatch()这个函数中,在epoll_wait返回以后,还将有事件的套接字添加到了一个队列中,将套接字添加到队列中的函数是event_active。在处理这些I/O时,只需将激活队列中的所用事件都处理即可。当然这只是考虑libevent只处理I/O事件的过程。

     如何将定时器也添加到I/O多路复用这个机制中呢?

     定时器事件需要一个时间的限制,在这里很容易添加进去,只要在epoll_wait这个函数等待的事件是所有定时事件中时间最小的那个时间即可,在event_base_loop函数中,timeout_next函数就是为了在最小堆中找出最小时间,以这个时间在epoll_wait中,在evsel->dispatch返回后,timeout_process()函数将需要处理的定时器事件都添加到激活队列中。这样就完成了定时器事件和I/O事件的融合。


   如何将信号处理事件添加到I/O复用机制中?

     由于信号处理这个不知道什么时候到,没有办法添加,但是只要能偶在信号到以后能够马上返回得到处理,这样既可,那么怎么可以让信号到达后,epoll_wait能够返回呢,也可以这样,弄一个套接字对,一个是读,一个是写,当有信号到达,写套接字发送一个消息,然后让读套接字存放到epoll_wait所监听的套接字集合中,那么如果信号到达,epoll_wait也会返回,在evsel->dispatch中调用epoll_wait,在这个函数返回之后,evsignal_process()函数直接处理信号(这里的处理信号并不是直接执行,而是遍历信号事件链表,将这个信号添加到激活链表中,信号的处理都是在所有的事件返回以后的再处理,这段代码在event_base_loop()函数中)。这样就完成了信号和I/O事件的融合。

libevent中,初始化阶段并不在注册读socket的读事件,而是在注册信号阶段才会测试并注册。

libevent中,检查I/O事件是在各个系统I/O机制的dispatch()函数中完成的,该diapatch()函数在event_base_loop()函数中被调用


    这样做,是为了什么:

   方便;这三类事件都是一样的架构,并没有分离,I/O、定时器事件、信号事件,都是在系统调用后得到处理,三类事件都是从激活队列中取出事件,根据事件的类型,调用其回调函数。

  统一:三类事件分别有相应的处理方法,evsignal_process()是处理信号的函数,从信号时间队列中取出相应事件,然后将事件添加到激活队列中。

            timeout_process()函数将定时器事件从最小堆中找到合适的事件,然后添加到激活队列中,在epoll_wait()函数返回后,将I/O事件添加到激活队列中。

            三类事件分三个地方添加!!!!