libevent之event
就如libevent官网上所写的“libevent - an event notification library”,libevent就是一个基于事件通知机制的库,可以看出event是整个库的核心。event就是Reactor框架中的事件处理程序组件(event_handler),它提供了函数接口,供Reactor在事件发生时调用,以执行相应的事件处理,通常它会绑定一个有效的句柄。
event结构体
event结构体定义在<event2/event_struct.h>中:
- struct event {
- TAILQ_ENTRY(event) ev_active_next;
- TAILQ_ENTRY(event) ev_next;
- /* for managing timeouts */
- union {
- TAILQ_ENTRY(event) ev_next_with_common_timeout;
- int min_heap_idx;
- } ev_timeout_pos;
- evutil_socket_t ev_fd;
- struct event_base *ev_base;
- union { // I/O事件和信号事件不能同时设置
- /* used for io events */
- struct {
- TAILQ_ENTRY(event) ev_io_next;
- struct timeval ev_timeout;
- } ev_io;
- /* used by signal events */
- struct {
- TAILQ_ENTRY(event) ev_signal_next;
- short ev_ncalls;
- /* Allows deletes in callback */
- short *ev_pncalls;
- } ev_signal;
- } _ev;
- short ev_events;
- short ev_res; /* result passed to event callback */
- short ev_flags;
- ev_uint8_t ev_pri; /* smaller numbers are higher priority */
- ev_uint8_t ev_closure;
- struct timeval ev_timeout;
- /* allows us to adopt for different types of events */
- void (*ev_callback)(evutil_socket_t, short, void *arg);
- void *ev_arg;
- };
在原文档中,作者对event结构体做了详细的解释(英文):
- /**
- * @struct event
- *
- * Structure to represent a single event.
- *
- * An event can have some underlying condition it represents: a socket
- * becoming readable or writeable (or both), or a signal becoming raised.
- * (An event that represents no underlying condition is still useful: you
- * can use one to implement a timer, or to communicate between threads.)
- *
- * Generally, you can create events with event_new(), then make them
- * pending with event_add(). As your event_base runs, it will run the
- * callbacks of an events whose conditions are triggered. When you
- * longer want the event, free it with event_free().
- *
- * In more depth:
- *
- * An event may be "pending" (one whose condition we are watching),
- * "active" (one whose condition has triggered and whose callback is about
- * to run), neither, or both. Events come into existence via
- * event_assign() or event_new(), and are then neither active nor pending.
- *
- * To make an event pending, pass it to event_add(). When doing so, you
- * can also set a timeout for the event.
- *
- * Events become active during an event_base_loop() call when either their
- * condition has triggered, or when their timeout has elapsed. You can
- * also activate an event manually using event_active(). The even_base
- * loop will run the callbacks of active events; after it has done so, it
- * marks them as no longer active.
- *
- * You can make an event non-pending by passing it to event_del(). This
- * also makes the event non-active.
- *
- * Events can be "persistent" or "non-persistent". A non-persistent event
- * becomes non-pending as soon as it is triggered: thus, it only runs at
- * most once per call to event_add(). A persistent event remains pending
- * even when it becomes active: you'll need to event_del() it manually in
- * order to make it non-pending. When a persistent event with a timeout
- * becomes active, its timeout is reset: this means you can use persistent
- * events to implement periodic timeouts.
- *
- * This should be treated as an opaque structure; you should never read or
- * write any of its fields directly. For backward compatibility with old
- * code, it is defined in the event2/event_struct.h header; including this
- * header may make your code incompatible with other versions of Libevent.
- *
- * @see event_new(), event_free(), event_assign(), event_get_assignment(),
- * event_add(), event_del(), event_active(), event_pending(),
- * event_get_fd(), event_get_base(), event_get_events(),
- * event_get_callback(), event_get_callback_arg(),
- * event_priority_set()
- */
注意上述中提到的pending和active的区别。pending表示的是监听事件的列表,而active表示的是已激活事件的列表。
下面简单解释一下结构体中重要字段的含义:
1)ev_events:说明要监听的事件类型(event支持I/O、超时和信号3种事件类型,),它的值可由以下字段位与而成:
- /**
- * @name event flags
- *
- * Flags to pass to event_new(), event_assign(), event_pending(), and
- * anything else with an argument of the form "short events"
- */
- /**@{*/
- /** Indicates that a timeout has occurred. It's not necessary to pass
- * this flag to event_for new()/event_assign() to get a timeout. */
- #define EV_TIMEOUT 0x01
- /** Wait for a socket or FD to become readable */
- #define EV_READ 0x02
- /** Wait for a socket or FD to become writeable */
- #define EV_WRITE 0x04
- /** Wait for a POSIX signal to be raised*/
- #define EV_SIGNAL 0x08
- /**
- * Persistent event: won't get removed automatically when activated.
- *
- * When a persistent event with a timeout becomes activated, its timeout
- * is reset to 0.
- */
- #define EV_PERSIST 0x10
- /** Select edge-triggered behavior, if supported by the backend. */
- #define EV_ET 0x20
- /**@}*/
2)ev_next、ev_active_next、ev_next_with_common_timeout、ev_io_next和ev_signal_next都是双向链表节点指针。它们是libevent对不同事件类型和在不同的时期,对事件的管理时使用到的字段。
3)min_heap_idx或ev_next_with_common_timeout指明超时事件在小根堆中的索引或在timeout list中的位置。
4)ev_base该事件所属的反应堆实例,这是一个event_base结构体。
5)ev_fd,对于I/O事件,是绑定的文件描述符;对于signal事件,是绑定的信号。
6)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 0x80 // event已被初始化
7)ev_callback,event的回调函数,被ev_base调用,执行事件处理程序,这是一个函数指针,原型为:
- void (*ev_callback)(int fd, short events, void *arg)
其中参数fd对应于ev_fd;events对应于ev_events;arg对应于ev_arg;
8)ev_arg:void*,表明可以是任意类型的数据,在设置event时指定;
9)ev_ncalls:事件就绪执行时,调用ev_callback的次数,通常为1;
10)ev_pncalls:指针,通常指向ev_ncalls或者为NULL;
11)ev_res:记录了当前激活事件的类型;
libevent对event的管理
libevent对event的管理如下图所示:
每次当有事件event转变为就绪状态时,libevent就会把它移入到active event list[priority]中,其中priority是event的优先级;接着libevent会根据自己的调度策略选择就绪事件,调用其cb_callback() 函数执行事件处理,并根据就绪的句柄和事件类型填充cb_callback函数的参数。
事件属性设置接口函数
在libevent,有几个函数可以用于设置事件的属性:
1. event_set
用于设置event属性的event_set函数实际上是调用了event_assign。
- void
- event_set(struct event *ev, evutil_socket_t fd, short events,
- void (*callback)(evutil_socket_t, short, void *), void *arg)
- {
- int r;
- r = event_assign(ev, current_base, fd, events, callback, arg);
- EVUTIL_ASSERT(r == );
- }
而event_assign函数的定义为:
- int
- event_assign(struct event *ev, struct event_base *base, evutil_socket_t fd, short events, void (*callback)(evutil_socket_t, short, void *), void *arg)
- {
- if (!base)
- base = current_base;
- _event_debug_assert_not_added(ev);
- ev->ev_base = base;
- ev->ev_callback = callback;
- ev->ev_arg = arg;
- ev->ev_fd = fd;
- ev->ev_events = events;
- ev->ev_res = ;
- ev->ev_flags = EVLIST_INIT;
- ev->ev_ncalls = ;
- ev->ev_pncalls = NULL;
- if (events & EV_SIGNAL) {
- if ((events & (EV_READ|EV_WRITE)) != ) {
- event_warnx("%s: EV_SIGNAL is not compatible with "
- "EV_READ or EV_WRITE", __func__);
- return -;
- }
- ev->ev_closure = EV_CLOSURE_SIGNAL;
- } else {
- if (events & EV_PERSIST) {
- evutil_timerclear(&ev->ev_io_timeout);
- ev->ev_closure = EV_CLOSURE_PERSIST;
- } else {
- ev->ev_closure = EV_CLOSURE_NONE;
- }
- }
- min_heap_elem_init(ev);
- if (base != NULL) {
- /* by default, we put new events into the middle priority */
- ev->ev_pri = base->nactivequeues / ;
- }
- _event_debug_note_setup(ev);
- return ;
- }
其中,参数为:
另外,我们也可以在创建新事件的时候设定事件属性,具体函数是event_new。而event_new实际也是调用了event_assign来实现的,不同的是event_new需要先给事件分配空间:
- struct event *
- event_new(struct event_base *base, evutil_socket_t fd, short events, void (*cb)(evutil_socket_t, short, void *), void *arg)
- {
- struct event *ev;
- ev = mm_malloc(sizeof(struct event));
- if (ev == NULL)
- return (NULL);
- if (event_assign(ev, base, fd, events, cb, arg) < ) {
- mm_free(ev);
- return (NULL);
- }
- return (ev);
- }
2. event_base_set
在默认情况下,事件event会被注册到一个全局event_base指针current_base。使用该函数可以指定不同的event_base。如果一个进程中存在多个libevent实例,必须要调用该函数为event设置不同的event_base。
该函数的定义如下:
- int
- event_base_set(struct event_base *base, struct event *ev)
- {
- /* Only innocent events may be assigned to a different base */
- if (ev->ev_flags != EVLIST_INIT)
- return (-);
- _event_debug_assert_is_setup(ev);
- ev->ev_base = base;
- ev->ev_pri = base->nactivequeues/;
- return ();
- }
3. event_priority_set
在默认情况下,所有的event的优先级都被设定为 active event list 长度的一半(nactivequeues / 2)。该函数可用于设定event的优先级。优先级的数值越小,表示优先级越高。另外,函数event_base_priority_init可用于设定优先级的最大值。
event_priority_set的函数定义如下:
- /*
- * Set's the priority of an event - if an event is already scheduled
- * changing the priority is going to fail.
- */
- int
- event_priority_set(struct event *ev, int pri)
- {
- _event_debug_assert_is_setup(ev);
- if (ev->ev_flags & EVLIST_ACTIVE)
- return (-);
- if (pri < || pri >= ev->ev_base->nactivequeues)
- return (-);
- ev->ev_pri = pri;
- return ();
- }
事件相关的其他常用接口函数
1. event_new
创建事件(涉及内存分配)。
1. event_add
添加事件到event_base。
2. event_del
将事件从监听列表中移除。
3. event_free
释放由event_new创建的事件(内存)。从其定义可看出其与event_del的区别:
- void
- event_free(struct event *ev)
- {
- _event_debug_assert_is_setup(ev);
- /* make sure that this event won't be coming back to haunt us. */
- event_del(ev);
- _event_debug_note_teardown(ev);
- mm_free(ev);
- }
3. event_callback_fn
事件的回调函数,用于执行具体的I/O操作。其定义如下:
- /**
- A callback function for an event.
- It receives three arguments:
- @param fd An fd or signal
- @param events One or more EV_* flags
- @param arg A user-supplied argument.
- @see event_new()
- */
- typedef void (*event_callback_fn)(evutil_socket_t, short, void *);
关于超时和信号事件的特殊接口函数
为了方便对超时和信号事件的处理,libevent特别为它们定义了接口函数(实际是对通用函数的封装)。
超时事件:
- /**
- @name evtimer_* macros
- Aliases for working with one-shot timer events */
- /**@{*/
- #define evtimer_assign(ev, b, cb, arg) \
- event_assign((ev), (b), -, , (cb), (arg))
- #define evtimer_new(b, cb, arg) event_new((b), -1, 0, (cb), (arg))
- #define evtimer_add(ev, tv) event_add((ev), (tv))
- #define evtimer_del(ev) event_del(ev)
- #define evtimer_pending(ev, tv) event_pending((ev), EV_TIMEOUT, (tv))
- #define evtimer_initialized(ev) event_initialized(ev)
- /**@}*/
信号事件:
- /**
- @name evsignal_* macros
- Aliases for working with signal events
- */
- /**@{*/
- #define evsignal_add(ev, tv) event_add((ev), (tv))
- #define evsignal_assign(ev, b, x, cb, arg) \
- event_assign((ev), (b), (x), EV_SIGNAL|EV_PERSIST, cb, (arg))
- #define evsignal_new(b, x, cb, arg) \
- event_new((b), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg))
- #define evsignal_del(ev) event_del(ev)
- #define evsignal_pending(ev, tv) event_pending((ev), EV_SIGNAL, (tv))
- #define evsignal_initialized(ev) event_initialized(ev)
- /**@}*/
参考资料
libevent之event的更多相关文章
- libevent book——event | Gaccob的博客
libevent book——event | Gaccob的博客 libevent book——event 发表于 2013 年 2 月 22 日 由 gaccob 原文地址:http://www.w ...
- libevent核心-event和event_base结构体
参考:http://blog.csdn.net/yusiguyuan/article/category/2171081/2 http://blog.csdn.net/sparkliang/articl ...
- libevent(五)event
libevent使用struct event来表示一个事件. #define evutil_socket_t int #define ev_uint8_t unsigned char #define ...
- (转)Libevent(2)— event、event_base
转自:http://name5566.com/4198.html 参考文献列表:http://www.wangafu.net/~nickm/libevent-book/ 此文编写的时候,使用到的 Li ...
- libevent源码学习(15):信号event的处理
目录信号event处理流程与信号event相关的结构体初始化工作创建一个信号event添加一个信号event信号回调函数信号event的激活 Libevent中的event,主要分为三大类 ...
- 【转】libevent源码分析
libevent源码分析 转自:http://www.cnblogs.com/hustcat/archive/2010/08/31/1814022.html 这两天没事,看了一下Memcached和l ...
- libevent源码深度剖析
原文地址: http://blog.csdn.net/sparkliang/article/details/4957667 第一章 1,前言 Libevent是一个轻量级的开源高性能网络库,使用者众多 ...
- libevent系列文章
Libevent 2 提供了 bufferevent 接口,简化了编程的难度,bufferevent 实际上是对底层事件核心的封装,因此学习 bufferevent 的实现是研究 Libevent 底 ...
- (转)Libevent(1)— 简介、编译、配置
转自:http://name5566.com/4190.html 参考文献列表:http://www.wangafu.net/~nickm/libevent-book/ 此文编写的时候,使用到的 Li ...
随机推荐
- 安卓 LayoutInflater参数作用
方法重载1 public View inflate (int resource, ViewGroup root, boolean attachToRoot) 方法重载2 public View inf ...
- Android自定义底部带有动画的Dialog
Android自定义底部带有动画的Dialog 效果图 先看效果图,是不是你想要的呢 自定义Dialog package --.view; import android.app.Dialog; imp ...
- Objective-C点语法
Objective-C点语法 点语法可以简单的理解成是为了让Java等语言的开发人员能够快速适应OC语言而添加的一个新写法 因为Java里没有指针,也没有[xxx xxx]这种调用方式,都是使用点xx ...
- Android广播的发送与接收
Android广播的发送与接收 效果图 广播发送 广播分为有序广播和无序广播 有序广播与无序广播的区别 无序广播:只要是广播接收者指定了接收的事件类型,就可以接收到发送出来的广播消息.不能修改消息. ...
- 关于bitmap你不知道的一些事
1.计算机表示图形的几种方式 1)BMP :几乎不进行压缩 占用空间比较大 2)JPG : 在BMP的基础上对相邻的像素进行压缩,占用空间比BMP小 3) PNG : 在JPG的基础上进一步压缩 占用 ...
- Swift3的playground中对UI直接测试支持的改变
我们知道在Xcode的playground中不仅可以测试console代码,还可以测试UI代码,甚至我们可以测试SpriteKit中的场景,有兴趣的童鞋可以看我之前写的这一篇blog: Xcode的p ...
- 在Mac上搭建React Native开发环境
概述 前面我们介绍过在window环境下开发React Native项目,今天说说怎么在mac上搭建一个RN的开发环境. 配置mac开发环境 基本环境安装 1.先安装Homebrew:用于安装Node ...
- Ruby 连接MySQL数据库
使用Ruby连接数据库的过程还真的是坎坷,于是写点文字记录一下. 简介 Ruby简介 RubyGems简介 包管理之道 比较著名的包管理举例 细说gem 常用的命令 准备 驱动下载 dbi mysql ...
- ubuntu蓝牙音响配对成功但在声音设置中无法设置 解决
ubuntu蓝牙音响配对成功但在声音设置中无法设置 解决 首先,连接蓝牙 但是,在声音设置中如下: 都没有发现设备??? 打开终端输入: ~$ pactl load-module module-blu ...
- java之IO流详解(二)
好了,昨天讲了字节流,现在我们就来讲字符流吧... 字符流可以这样理解,字符流 = 字节流 + 编码表,目的是为了更好的操作中文字符(注:字符流只可以可以操作字符类型的文件,不能操作影音图像文件,要操 ...