libevent总结(上)

最近想编写diyDB,所以想借鉴一下libevent的事件机制。之前也看过libevent的部分代码,但长时间不用已经忘的干净了。今天借此机会温习一下libevent,在此写个总结。

一、简介

libevent的最大看点是事件驱动和跨平台,当然还有轻量级。但是,其核心绝对是事件驱动机制。libevent的事件分为I/O事件,定时器事件和信号事件。

二、使用的简单介绍

下面是libevent的一个定时器事件的应用

1)首先初始化 libevent 库,并保存返回的指针

struct event_base * base = event_init();

实际上这一步相当于初始化一个 Reactor 实例;在初始化 libevent 后,就可以注册事件了。注意:在支持epoll的系统中每一个 Reactor 实例归根到底管理着一个epoll_wait().

2)初始化事件 event,设置回调函数和关注的事件

evtimer_set(&ev, timer_cb, NULL);

事实上这等价于调用 event_set(&ev, -1, 0, timer_cb, NULL);

event_set 的函数原型是:

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_set 时的 fd, event 和 arg;

arg:传递给 cb 函数指针的参数;

由于定时事件不需要 fd,并且定时事件是根据添加时(event_add)的超时值设定的,因此

这里 event 也不需要设置。

这一步相当于初始化一个 event handler,在 libevent 中事件类型保存在 event 结构体中。

注意:libevent 并不会管理 event 事件集合,这需要应用程序自行管理;

3)设置 event 从属的 event_base

event_base_set(base, &ev);

这一步相当于指明 event 要注册到哪个 event_base 实例上;实际上就是指定注册到哪个epoll的事件注册表上

4)是正式的添加事件的时候了

event_add(&ev, timeout);

基本信息都已设置完成,只要简单的调用 event_add()函数即可完成,其中 timeout 是定时值;这一步相当于调用

Reactor::register_handler()函数注册事件。这里就是正式地将事件ev注册到对应的事件注册表上

5)程序进入无限循环,等待就绪事件并执行事件处理

event_base_dispatch(base);//核心就是while(){epoll_wait()};

三、事件处理流程的简单介绍

1)首先应用程序准备并初始化 event,设置好事件类型和回调函数;

2)向 libevent 添加该事件 event。对于定时事件,libevent 使用一个小根堆管理,key 为超

时时间;对于 Signal 和 I/O 事件,libevent 将其放入到等待链表(wait list)中,这是一

个双向链表结构;

3)程序调用 event_base_dispatch()系列函数进入无限循环,等待事件,以 select()函数为例;

每次循环前 libevent 会检查定时事件的最小超时时间 tv,根据 tv 设置 epoll()的最大等

待时间,以便于后面及时处理超时事件;

当 epoll()返回后,首先检查超时事件,然后检查 I/O 事件;

Libevent 将所有的就绪事件,放入到激活链表中;

然后对激活链表中的事件,调用事件的回调函数执行事件处理;

三、reactor模式

说起libevent模式就不得不说reactor模式,因为libevent的事件处理机制就是reactor模式

1.概念

Reactor 释义“反应堆”,是一种事件驱动机制。先向Reactor注册事件和回调函数,然后等事件发生时,reactor自动去调用对应的回调函数去处理事件(I/O 读写、定时和信号)。(注:通函数调用的是:应用程序不是主动的调用某个 API 完成处理)

2.优点

Reactor 模式是编写高性能网络服务器的必备技术之一,它具有如下的优点:

1)响应快,不必为单个同步时间所阻塞,虽然 Reactor 本身依然是同步的;(回调函数中的操作最好是非阻塞的)

2)编程相对简单,可以最大程度的避免复杂的多线程及同步问题,并且避免了多线程/进程的切换开销;

3)可扩展性,可以方便的通过增加 Reactor 实例个数来充分利用 CPU 资源;

4)可复用性,reactor 框架本身与具体事件处理逻辑无关,具有很高的复用性;

3.reactor框架

使用 Reactor 模型,必备的几个组件:事件源、Reactor 框架、多路复用机制和事件处理程序

1) 事件源

Linux 上是文件描述符,Windows 上就是 Socket 或者 Handle 了,这里统一称为“句柄集”;程序在指定的句柄上注册关心的事件,比如 I/O 事件。

2) event demultiplexer——事件多路分发机制

由操作系统提供的 I/O 多路复用机制,比如 select 和 epoll。程序首先将其关心的句柄(事件源)及其事件注册到 event demultiplexer 上;当有事件到达时,event demultiplexer 会发出通知“在已经注册的句柄集中,一个或多个句柄的事件已经就绪”;程序收到通知后,就可以在非阻塞的情况下对事件进行处理了。对应到 libevent 中,依然是 select、poll、epoll 等,但是 libevent 使用结构体 eventop 进行了封装,以统一的接口来支持这些
I/O 多路复用机制,达到了对外隐藏底层系统机制的目的。

3) Reactor——反应器

Reactor,是事件管理的接口,内部使用 event demultiplexer 注册、注销事件;并运行事件循环,当有事件进入“就绪”状态时,调用注册事件的回调函数处理事件。对应到 libevent 中,就是 event_base 结构体。

4) Event Handler——事件处理程序

事件处理程序提供了一组接口,每个接口对应了一种类型的事件,供 Reactor 在相应的事件发生时调用,执行相应的事件处理。通常它会绑定一个有效的句柄。对应到 libevent 中,就是 event 结构体

四、事件event

Libevent 是基于事件驱动(event-driven)的,从名字也可以看到 event 是整个库的核心。event 就是 Reactor 框架中的事件处理程序组件;它提供了函数接口,供 Reactor 在事件发生时调用,以执行相应的事件处理,通常它会绑定一个有效的句柄。事实上,这里的event已经包括了事件源和事件处理函数的信息

struct event {
TAILQ_ENTRY (event) ev_next;//双向链表节点的指针,表示该事件在i/o事件的已注册事件链表中的位置
TAILQ_ENTRY (event) ev_active_next;//表示该事件在激活事件的已注册事件链表中的位置,如果挂上了,说明这个事件已经发生了,正在等待相应的事件处理函数被执行
TAILQ_ENTRY (event) ev_signal_next;//双向链表节点的指针,表示该事件在信号事件的已注册事件链表中的位置
unsigned int min_heap_idx; //如果该事件是超时事件,min_heap_idx表示该事件在超时事件小顶堆中的索引
struct event_base *ev_base;//ev_base 该事件所属的反应堆实例,这是一个 event_base 结构体
int ev_fd;//ev_fd,对于 I/O 事件,是绑定的文件描述符;对于 signal 事件,是绑定的信号(实际上是有名管道描述符);
short ev_events;//event关注的事件类型
short ev_ncalls;//事件就绪执行时,调用 ev_callback 的次数,通常为 1;
short *ev_pncalls;//指针,通常指向 ev_ncalls 或者为 NULL
/* Allows deletes in callback */
struct timeval ev_timeout;//如果该事件是超时事件,ev_timeout表示超时事件的超市值
int ev_pri;
/* smaller numbers are higher priority */
void (*ev_callback)(int, short, void *arg);//event 的回调函数,被 ev_base 调用,执行事件处理程序
void *ev_arg;
int ev_res;//记录了当前激活事件的类型;
/* result passed to event callback */
int ev_flags;
};
1)ev_events:event关注的事件类型,它可以是以下3种类型:
I/O事件: EV_WRITE和EV_READ
定时事件:EV_TIMEOUT
信号:EV_SIGNAL
辅助选项:EV_PERSIST,表明是一个永久事件
Libevent中的定义为:
#define  EV_TIMEOUT  0x01
#define EV_READ 0x02
#define EV_WRITE 0x04
#define EV_SIGNAL 0x08
#define EV_PERSIST 0x10/*永久事件,即每次事件发生并且处理完后,自动加入到相应队列中*/

2)eb_flags:libevent 用于标记 event 信息的字段,表明其当前的状态,可能的值有:

#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 // event 已被初始化

event的管理流程解析:

每次当有事件 event 转变为就绪状态时, libevent 就会把它移入到 active event list[priority](就绪事件有优先级)中,其中 priority 是 event 的优先级;接着 libevent 会根据自己的调度策略选择就绪事件,调用其 cb_callback()函数执行事件处理;并根据就绪的句柄和事件类型填充 cb_callback 函数的参数。

在向libevent添加一个事件前必须先初始化一个事件,即设置事件。这通过调用 libevent 提供的函数有:event_set(), event_base_set(), event_priority_set()来完成。注:libevent 有一个全局 event_base 指针 current_base,默认情况下事件 ev将被注册到 current_base 上,使用该函数可以指定不同的 event_base;如果一个进程中存在多个libevent 实例,则必须要调用该函数为 event
设置不同的 event_base;

时间: 2024-10-23 17:35:44

libevent总结(上)的相关文章

【转】libevent和基于libevent的网络编程

转自: http://www.cnblogs.com/nearmeng/p/4043548.html 1 libevent介绍和安装 介绍 libevent是一个轻量级的基于事件驱动的高性能的开源网络库,并且支持多个平台,对多个平台的I/O复用技术进行了封装,当我们编译库的代码时,编译的脚本将会根据OS支持的处理事件机制,来编译相应的代码,从而在libevent接口上保持一致. 在当前的服务器上,面对的主要问题就是要能处理大量的连接.而通过libevent这个网络库,我们就可以调用它的API来很

libevent 源码深度剖析十三

libevent 源码深度剖析十三 -- libevent 信号处理注意点 前面讲到了 libevent 实现多线程的方法,然而在多线程的环境中注册信号事件,还是有一些情况需要小心处理,那就是不能在多个 libevent 实例上注册信号事件.依然冠名追加到 libevent 系列. 以 2 个线程为例,做简单的场景分析. 1 首先是创建并初始化线程 1 的 libevent 实例 base1 ,线程 1 的 libevent 实例 base2 : 2 在 base1 上注册 SIGALRM 信号

一起读读libevent的源代码:Libevent 第一章 设置libevent

某人曾提醒我要多读源代码,我就选了libevent 2.1.8稳定版的源代码来读. 读了一会,纯看源代码里面的东西,还挺无聊的.所以我就开始,便看他们的编程教程: http://www.wangafu.net/~nickm/libevent-book/ 然后每遇到实现,我就跑去源代码中看别人怎么做到的. 这样还是比较有趣的,一个一个小目标的去做,直到这个事情是为什么而做. 我之前,已经把编程的指导粗略看过一边,也是边犯困边看,再开始看源代码,昨天睡了十次八次,才看了很小的一部分. 这样太慢了,而

Libevent教程001: 简介与配置

本文内容大致翻译自 libevent-book, 但不是照本翻译. 成文时, libevent最新的稳定版为 2.1.8 stable. 即本文如无特殊说明, 所有描述均以 2.1.8 stable 版本为准. 本文为系列文章的第一篇, 对应libevent-book的 chapter 0 + chapter 1 + R0 + R1 0. 前提条件 这个文档是对libevent的介绍与指导, 阅读文档需要你具有以下的能力: 你精通C语言 你至少了解Unix网络编程. 你会安装libevent 你

Centos下安装memcached+memcached教程

Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度.Memcached基于一个存储键/值对的hashmap.其守护进程(daemon )是用C写的,但是客户端可以用任何语言来编写,并通过memcached协议与守护进程通信. Memcache是一个高性能的分布式的内存对象缓存系统,通过在内存里维护一个统一的巨大的hash表,它能够用来存储各种格式的数据.简单的说就是将

分布式缓存系统 Memcached 主线程之main函数

前两节中对工作线程的工作流程做了较为详细的分析,现把其主要流程总结为下图: 接下来本节主要分析主线程相关的函数设计,主函数main的基本流程如下图所示: 对于主线程中的工作线程的初始化到启动所有的工作线程前面已经做了分析,后面的创建监听socket.注册监听socket的libevent事件.启动主线程的libevent事件循环,就是接下来的内容了. 其中主要调用的函数是server_sockets,该函数从配置参数setting.inner字符串中依次提取出一个ip或者一个hostname(一

Memcached网络模型

之前用libevent开发了一个流媒体服务器.用线程池实现的.之后又看了memcached的网络相关实现,今天来整理一下memcached的实现流程. memcached不同于Redis的单进程单线程,是采用多线程的工作方式.有一个主线程,同时维护了一个线程池(工作线程).worker thread工作线程和main thread主线程之间主要通过pipe来进行通信.因为用了libevent,所以感觉比Redis稍微庞大点,没有在生产环境对比过Redis和memcached,所以也不好说什么性能

liunx系统安装memcached

首先弄清memcache .memcached的差别,相差一个字母,总结下: Memcache是什么?    Memcache是一个自由和开放源代码.高性能.分配的内存对象缓存系统.用于加速动态web应用程序,减轻数据库负载.它可以应对任意多个连接,使用非阻塞的网络IO.由于它的工作机制是在内存中开辟一块空间,然后建立一个Hash表,Memcached自管理这些Hash表.Memcache官方网站:http://memcached.org/ Memcached又是什么?    Memcache是

Windows 上静态编译 Libevent 2.0.10 并实现一个简单 HTTP 服务器(无数截图)

[文章作者:张宴 本文版本:v1.0 最后修改:2011.03.30 转载请注明原文链接:http://blog.s135.com/libevent_windows/] 本文介绍了如何在 Windows 操作系统中,利用微软 Visual Studio 2005 编译生成 Libevent 2.0.10 静态链接库,并利用 Libevent 静态链接库,实现一个简单的 HTTP Web服务器程序:httpd.exe. 假设 Visual Studio 2005 的安装路径为“D:\Program

Windows 上静态编译 Libevent 2.0.10 并实现一个简单 HTTP 服务器(图文并茂,还有实例下载)

[文章作者:张宴 本文版本:v1.0 最后修改:2011.03.30 转载请注明原文链接:http://blog.s135.com/libevent_windows/] 本文介绍了如何在 Windows 操作系统中,利用微软 Visual Studio 2005 编译生成 Libevent 2.0.10 静态链接库,并利用 Libevent 静态链接库,实现一个简单的 HTTP Web服务器程序:httpd.exe. 假设 Visual Studio 2005 的安装路径为“D:\Program