简介
libevent是一个开源的高性能的网络库
优点:
事件驱动(event-driven),高性能
轻量级,专注于网络
源代码相当精炼,易读
夸平台,支持windows,linux等多个平台
支持I/0,定时器和信号等事件
注册事件优先级
Reactor
基本使用场景和事件流程
1、初始化libevent,并保存返回的指针:struct event_base *base = event_init() ;
#include <event.h>
#include <stdio.h>
struct event ev;
struct timeval tv;
struct event ev1 ;
struct timeval tv1;
void time_cb(int fd, short event, void *argc)
{
printf("timer1 wakeup fd = %d\n",fd);
event_add(&ev, &tv); // reschedule timer
}
void time_cb2(int fd,short event ,void *argc)
{
printf("timer wakeup fd = %d\n",fd );
event_add(&ev1, &tv1);
}
int main()
{
struct event_base *base = event_init();
tv.tv_sec = 1;
tv.tv_usec = 0;
tv1.tv_sec = 2;
tv1.tv_usec = 0;
event_set(&ev, -1, 0, timer_cb, NULL);
evtimer_set(&ev1,time_cb2,NULL); //宏等价于evnet_set(&ev1,-1,0,time_cb2,NULL);
event_add(&ev, &tv);
event_add(&ev1,&tv1);
event_base_dispatch(base);
return 0 ;
}
libevent源代码组织
truct event
{
TAILQ_ENTRY(event) ev_active_next;
TAILQ_ENTRY(event) ev_next;
union
{
TAILQ_ENTRY(event) ev_next_with_common_timeout;
int min_heap_idx;
} ev_timeout_pos;
evutil_socket_t ev_fd; //对应I/0事件是文件描述符,对于signal事件是绑定的信号
struct event_base *ev_base; //该事件所属反应堆实例;
union
{
//IO事件
struct
{
TAILQ_ENTRY(event) ev_io_next;
struct timeval ev_timeout;
} ev_io;
//signal events事件
struct
{
TAILQ_ENTRY(event) ev_signal_next;
short ev_ncalls; //事件就绪是调用ev_callback的次数,通常为1
short *ev_pncalls; //指针,指向 ev_ncalls或NULL
} ev_signal;
} _ev;
short ev_events; //event关注的事件类型
short ev_res; //记录当前激活事件的类型
short ev_flags; //标记event信息的字段,表明其当前的状态
ev_uint8_t ev_pri; // smaller numbers are higher priority
ev_uint8_t ev_closure;
struct timeval ev_timeout;
void (*ev_callback)(evutil_socket_t, short, void *arg);//回调函数指针
void *ev_arg; //回调函数arg参数;
};
1、ev_events: event关注的事件类型,它可以是以下3种事件类型
#define EV_TIMEOUT 0x01
#define EV_READ 0x02
#define EV_WRITE 0x04
#define EV_SIGNAL 0x08
#define EV_PERSIST 0x10
事件类型可以使用"|"存在,但是信号和I/0事件不能同时设置
2、eb_flags字段的值
#define EVLIST_TIMEOUT 0x01 // event在time堆中
#define EVLIST_INSERTED 0x02 // event在已注册事件链表中
#define EVLIST_SIGNAL 0x04 // 未见使用
#define EVLIST_ACTIVE 0x08 // event在激活链表中
#define EVLIST_INTERNAL 0x10 // 内部使用标记
#define EVLIST_INIT 0x80 // event 已被初始化
事件设置接口函数
void event_set(struct event *ev, int fd, short events,void (*callback)(int, short, void *), void *arg);
/*
1.设置事件 ev 绑定的文件描述符或者信号,对于定时事件,设为-1 即可;
2.设置事件类型,比如 EV_READ|EV_PERSIST, EV_WRITE, EV_SIGNAL 等;
3.设置事件的回调函数以及参数 arg;
4.初始化其它字段,比如缺省的 event_base 和优先级;
*/
int event_base_set(struct event_base *base, struct event *ev);
/*
设置 event ev 将要注册到的 event_base;
libevent 有一个全局 event_base 指针 current_base,默认情况下事件 ev
将被注册到 current_base 上,使用该函数可以指定不同的 event_base;
如果一个进程中存在多个 libevent 实例,则必须要调用该函数为 event 设置不同的 event_base;
*/
int event_priority_set(struct event *ev, int pri)
//设置event ev的优先级,没什么可说的,注意的一点就是:当ev正处于就绪状态时,不能设置,返回-1。
初见事件处理框架
struct event_base
{
const struct eventop *evsel;
void *evbase; //是一个eventop实例对象
struct event_changelist changelist;
const struct eventop *evsigsel;
struct evsig_info sig; //管理信号的结构体
int virtual_event_count;
int event_count;
int event_count_active;
int event_gotterm;
int event_break;
int event_continue;
int event_running_priority;
int running_loop;
struct event_list *activequeues;
int nactivequeues;
struct common_timeout_list **common_timeout_queues;
int n_common_timeouts;
int n_common_timeouts_allocated;
struct deferred_cb_queue defer_queue;
struct event_io_map io;
struct event_signal_map sigmap;
struct event_list eventqueue; //保存了所有注册事件event的指针;
struct timeval event_tv;
struct min_heap timeheap;
struct timeval tv_cache;
#if defined(_EVENT_HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
struct timeval tv_clock_diff;
time_t last_updated_clock_diff;
#endif
#ifndef _EVENT_DISABLE_THREAD_SUPPORT
unsigned long th_owner_id;
void *th_base_lock;
struct event *current_event;
void *current_event_cond;
int current_event_waiters;
#endif
#ifdef WIN32
struct event_iocp_port *iocp;
#endif
enum event_base_config_flag flags;
int is_notify_pending;
evutil_socket_t th_notify_fd[2];
struct event th_notify;
int (*th_notify_fn)(struct event_base *base);
};
struct eventop
{
const char *name;
void *(*init)(struct event_base *); //初始化;
int (*add)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo);//注册事件;
int (*del)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo);//删除事件;
int (*dispatch)(struct event_base *, struct timeval *);//事件分发
void (*dealloc)(struct event_base *);//注销,释放资源
int need_reinit;
enum event_method_feature features;
size_t fdinfo_len;
};
每个I/0 demultiplex机制的实现都必须提供这5个函数接口来完成自身初始化、销毁、释放;对事件的注册、注销、和发布。
/*
功能:注册事件
参数:ev:要注册的事件, timeout :超时时间
返回 :失败返回-1
*/
int event_add(struct event *ev, const struct timeval *timeout);
/*
功能:删除事件 int event_del(struct event *ev);
参数:ev:要删除的事件
返回: 失败返回-1;
*/
int event_del(struct event *ev);
int event_base_loop(struct event_base *base, int loops);
void event_active(struct event *event, int res, short events);
void event_process_active(struct event_base *base);
事件住循环
集成信号处理
Socket pair是一个socket对,包含读socket和写socket。
Libevent提供了辅助函数evutil_socketpair()来创建一个socket pair。
int evutil_socketpair(int d, int type, int protocol, evutil_socket_t sv[2]);
为socket pair的读socket在libevent的event_base实例上注册一个persist的读事件。当写socket写入数据时,读socket就会得到通知,从而event_base就能相应的得到通知了。
Signal事件的管理通过结构体evsignal_info完成的,结构体的定义如下
struct evsignal_info
{
struct event ev_signal;//为socket pair的读socket向event_base注册读事件时使用的event结构体;
int ev_signal_pair[2];//socket pair 对
int ev_signal_added;//记录ev_signal事件是否已经注册
volatile sig_atomic_t evsignal_caught;//是否有信号发射的标记
struct event_list evsigevents[NSIG];//表示注册信号的链表
sig_atomic_t evsigcaught[NSIG];//记录每个信号的发射次数
#ifdef HAVE_SIGACTION
struct sigaction **sh_old; //记录原来signal函数的指针
#else
ev_sighandler_t **sh_old;
#endif
int sh_old_max;
};
注册、注销signal事件:
注册signal事件是通过evsignal_add(struct event* ev)函数完成的,libe对所有的信号注册同一个处理函数evsignal_handler()。
注册的过程:
1、取得ev要注册到的信号signo;
2、如果信号signo未被注册,那么就为signore注册信号处理函数evsignal_handler();
3、如果事件ev_signal还没有祖册,就注册ev_signal事件;
4、将事件ev添加到signo的event链表中
从signo中注销一个已经注册的signal事件,直接从已经注册事件的链表中移除即可。如果链表已经为NULL,那么就恢复旧的处理函数。
集成定时器事件
系统I/0的机制允许程序制定一个最大等待时间(也称为最大超时时间)timeout,即使没有事件发生它们能保证在timeout时间内返回。
跟距所有timer事件的最小超时时间来设置系统I/O的timeout时间;当系统I/O返回时,再激活所有就绪的timer事件就可以将Timer事件融合到系统的I/O机制中。
Libevent 使用堆来管理Timer事件,其key值就是事件的超时时间。
推荐网站:libevent源码深度剖析