libevent源码学习-----事件驱动流程分析

libevent中事件驱动的大体流程如下

/* 创建事件驱动 */
struct event_base* base = event_base_new(); 
/*
 *创建一个事件
 *@param base: 事件驱动
 *@param fd: event对应的文件描述符,通常是通过socket创建的套接字
 *@param EV_READ: 想要监听fd的哪些事件,EV_READ表示监听fd是否可读,也可以是EV_PERSIST代表这个event是永久事件,在调用一次回调函数后仍然继续监听,对应一次性event,调用后不再监听
 *@param cb: 当fd对应的事件发生后调用的回调函数,用户提供
 *@param arg: 传给回调函数cb的参数
 */
struct event* ev = event_new(base, fd, EV_READ|EV_PERSIST, cb, arg);

/* 
 *将event添加到事件驱动中,由base统一管理event,当event对应的fd的事件发生,会自动调用event对应的cb
 *NULL表示不设置超时时间,如果设置,则在规定时间之内没有发生事件就会停止监控,删除event,设置EV_PERSIST时还会重新将event添加到base中
 */
event_add(ev, NULL);

/* 开始事件驱动主循环 */
event_base_dispatch(base);

/* 释放base */
event_base_free(base);

创建事件驱动base时调用的event_base_new主要时用于对base进行初始化,包括初始化

  • 用于存放监听文件描述符的event的map
  • 用于存放监听信号的event的map
  • 用于存放具有超时时间的event的最小堆,超时时间为绝对时间,具体可以参考时间管理
  • 用于存放已注册event的队列
  • 用于存放相应事件发生(已激活)event的队列
  • 选择合适的io多路复用函数,可以参考io多路复用的封装和使用

创建事件event时调用的event_new主要是对event进行初始化,包括初始化

  • 文件描述符/信号值
  • 回调函数
  • 监听的事件
  • 绝对时间表示的超时时间(如果有的话)
  • 在激活队列中的优先级

总之就是各种初始化,没有什么特别之处,在设计过程中需要用到什么,就在struct event结构体中添加什么,然后在event_new中初始化什么 
event详解参考event操作

添加event到base时调用的event_add主要是将event注册到base中,包括

  • 添加event到注册队列中
  • 添加event到io map/signal map中,同时将其添加到io多路复用函数中,信号会单独处理
  • 添加event到最小堆中(如果设置了超时时间的话)

对信号的处理参考统一事件源 
第二个参数可以指定超时时长,可以参考时间管理

开启事件驱动主循环时调用的event_base_dispatch主要就是进入while循环,包括

  • 确认io多路复用函数的阻塞时长,方法是选取最小的绝对超时时间(最小堆堆顶)
  • 调用io多路复用函数,如果有某事件发生,将其添加到base的激活队列中,激活原因为事件发生
  • 判断最小堆中的event是否超时,如果超时,方法激活队列中,激活原因为超时
  • 按照优先级顺序处理激活队列中的event,调用对应的回调函数

选择io多路复用函数的阻塞时长为最小堆堆顶的原因:

  • 为了满足可以监控具有超时时间的event,必须为io函数提供一个阻塞时长,不能是NULL,否则会一直等到有事件发生
  • 因为是绝对时间,所以当堆顶的event超时,最小堆其他event不可能已经超时,所以不会影响其他具有超时时间的event,更不会影响没有超时时间的event
  • 综上选择堆顶超时时间最为合适

猜你喜欢

转载自my.oschina.net/mickelfeng/blog/1814789