libevent核心原理

Libevent 是一个轻量级的高性能网络库,事件驱动,基于 Reactor 模式。

对 Libevent 源码的阅读可围绕事件管理展开:

libevent核心原理_第1张图片
Libevent 对三类事件源进行了统一,用 event 结构体表示,每个 event 实例表示一个事件。

同时监听多个事件(比如监听多个 socket 上的可读事件),必然会有多个 event 实例,对这些 event 实例进行管理需要特定的数据结构,Libevent 使用了尾队列(本质上是双向链表)和小根堆管理所有事件。

libevent核心原理_第2张图片
各个数据结构的用途如下:

  • 已注册事件队列:所有事件添加时都会被插入到已注册事件队列
  • 激活事件队列:事件触发后,被插入激活事件队列
  • 信号事件队列:信号事件添加时会插入信号事件队列
  • 小根堆:定时器事件用小根堆管理,堆顶的定时器事件是超时时间最小的(最先超时)

向 Reactor(对应源码中的 event_base ) 中添加I/O、定时事件,需要调用 event_add 函数,信号事件调用 evsignal_addevsignal_add 最终也会调用 event_add

event_add
min_heap_reserve: 为定时事件分配堆空间
add: 注册事件到 I/O 复用
event_queue_insert: event 插入已注册队列
event_queue_remove: 如果定时事件已在小根堆中, 删除堆中旧的事件
event_queue_remove: 如果定时事件已在激活队列中, 从激活队列中删除该事件
evutil_timeradd: 计算超时时间
event_queue_insert: 插入小根堆

可以看到,所有被注册的事件(I/O、定时器、信号)都会插入已注册队列。除此之外,定时事件被插入小根堆,信号事件被插入信号事件链表(在 evsignal_add 函数中完成)。

当事件激活(即所监听的事件发生),该事件会被插入激活队列,随后激活队列中的事件被处理(事件处理函数被调用),这部分工作在事件循环中完成:

event_base_dispatch
event_base_loop
timeout_correct: 校正系统时间
timeout_next: 计算下一个超时时间
evutil_timerclear: 如果有未处理的激活事件, 让 I/O 复用立即返回, 不必等待
event_haveevents: 没有已注册事件, 则立刻退出
gettime: 时间缓存 -> event_tv
dispatch: 把就绪事件插入激活队列
gettime: 系统时间 -> 时间缓存
timeout_process: 将超时的定时事件插入激活队列中
event_process_active: 处理激活队列中的事件
epoll_wait
evsignal_process: 处理信号事件
event_active: 将就绪事件添加到激活队列
event_queue_remove: 若是永久事件, 从激活队列中移除
event_del: 若是非永久事件, 从所有相关链表中删除
callback: 调用事件回调函数

事件循环的函数调用关系稍显复杂,但其主要逻辑很简单:

  1. dispatch 函数调用 epoll_wait(只讨论使用了 epoll 的情况) 等待 I/O 事件发生
  2. epoll_wait 返回后,处理信号事件(epoll_wait 返回有可能是因为发生了信号事件,这和 Libevent 对信号事件的管理有关,详见信号事件管理)
  3. 将就绪的 I/O 事件添加到激活事件队列
  4. 将超时事件添加到激活事件队列
  5. event_process_active 统一处理激活事件队列中的事件(即调用每个事件对应的事件处理函数)

你可能感兴趣的:(C/C++,Linux系统编程,Linux)