libevent学习

——学习笔记

Posted by Samuel on July 27, 2017

目录

基本应用场景:使用libevent设置定时器

  1. 初始化libevent库,并保存返回的指针,用于注册事件
    struct event_base *base = event_init();
    
  2. 初始化事件event,设置回调函数和关注的事件,相当于初始化一个event handler,在libevent中事件类型保存在event结构体中
    evtimer_set(&ev, timer_cb, NULL);
    //等价于
    event_set(&ev, -1, 0, timer_cb, NULL);
    //函数原型
    void event_set(struct event *ev, int fd, short event, void (*cb)(int, short, void *), void *arg); 
    
    • ev:执行要初始化的event对象;
    • fd:该event绑定的“句柄”,对于信号事件,它就是关注的信号;
    • event:在该fd上关注的事件类型,可以是EV_READ, EV_WRITE, EV_SIGNAL
    • cb:这是一个函数指针,当fd上的事件event发生时,调用该函数执行处理,它有三个参数,调用时由event_base负责传入,按顺序,实际上就是event_setfdeventarg
    • arg:传递给cb函数指针的参数;
  3. 在event_base上注册event
    event_base_set(base, &ev);
    
  4. 添加事件(注册事件)
    event_add(&ev, timeout);
    
  5. 程序进入无限循环,等待就绪事件并执行事件处理
    event_base_dispatch(base);
    

代码如下:

struct event ev;
struct timeval tv;
void time_cb(int fd, short event, void *argc)
{
    printf("timer wakeup\n");
    event_add(&ev, &tv); // reschedule timer
}
int main()
{
    struct event_base *base = event_init();
    tv.tv_sec = 10; // 10s period
    tv.tv_usec = 0;
    evtimer_set(&ev, time_cb, NULL);
    event_add(&ev, &tv);
    event_base_dispatch(base);
}

libevent内部事件处理流程

应用程序向libevent注册一个事件之后,libevent内部处理流程如下:

  1. 初始化event,设置事件类型和回调函数:
  2. 向libevent添加该event。对于定时事件,libevent使用一个小根堆管理,key为超时时间;对于signal和I/O事件,libevent将其放入等待链表中(wait list),这是一个双向链表结构。
  3. 程序调用event_base_dispatch()系列函数进入无限循环,等待事件,以select()函数为例:
    1. 每次循环前libevent会检查定时事件的最小超时时间tv,根据tv设置select()的最大等待时间,以便于后面及时处理超时事件;
    2. 当select()返回后,首先检查超时事件,然后检查I/O事件;
    3. libevent将所有就绪事件,放入到激活链表中;
    4. 然后对激活链表中的事件,调用回调函数执行事件处理;

bufferevent

除了响应事件,libevent还提供了一种通用的I/O缓冲模式bufferevent。由一个底层的传输接口(套接字),一个读取缓冲区和一个写入缓冲区组成。

与通常的事件在底层传输端口已经就绪,可以读取或者写入的时候执行回调不同的是,bufferevent在读取或者写入了足够量的数据之后调用用户提供的回调,有多种共享公用接口的bufferevent类型。

每个bufferevent都有一个输入缓冲区和一个输出缓冲区,它们的类型都是struct evbuffer。有数据要写入到bufferevent时,添加数据到输出缓冲区;bufferevent中有数据供读取的时候,从输入缓冲区抽取数据。

每个bufferevent有两个数据相关的回调:一个读取回调和一个写入回调。默认情况下,从底层传输端口读取了任意量的数据之后会调用读取回调;输出缓冲区中足够量的数据被清空到底层传输端口后写入回调会被调用。通过调整bufferevent的读取和写入“水位”可以覆盖这些函数的默认行为。

基于套接字的Eventbuffer(常用)

理解:bufferevent相当于对event的进一步封装,使得程序员不必了解event具体是收发多少次,只需将消息发出即可。