libevent源码分析(四) 事件event

本文深入解析libevent框架,探讨其核心组件event结构体设计,包括事件类型、优先级、回调函数等关键字段。同时,详细介绍了事件管理及设置接口函数,如event_set、event_base_set和event_priority_set。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

l i b e v e n t libevent libevent是基于事件驱动的。从其名字也可以看出, e v e n t event event是整个 l i b e v e n t libevent libevent框架的核心。正如在libevent源码分析(二) Reactor模式中提到, e v e n t event event R e a c t o r Reactor Reactor模式中的事件处理组件:提供了函数接口,当有事件发生时,会调用相应的接口执行事件处理,通常会绑定一个有效的句柄。

event结构体

首先来看一下 e v e n t event event结构体的设计,在 e v e n t 2 / e v e n t event2/event event2/event_ s t r u c t . h struct.h 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;
    //事件处理器从属的event_base实例
	struct event_base *ev_base;

    union {
        /* used for io events */
        //I/O事件队列
        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 */
            //要么为NULL,要么指向ev_ncalls
            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 */
    //指定event_base执行事件处理器的回调函数时的行为
    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;
};

(1) 宏 T A I L Q TAILQ TAILQ_ E N T R Y ENTRY ENTRY是尾队列中的节点类型,定义在 c o m p a t / s y s / q u e u e . h compat/sys/queue.h compat/sys/queue.h文件中:

#define TAILQ_ENTRY(type)
struct {
	struct type* tqe_next; //下一个元素
	struct type** tqe_prev; //前一个元素的地址
}

(2) e v ev ev_ t i m e o u t timeout timeout_ p o s pos pos:这是一个联合体,仅用于定时事件处理

(3) e v ev ev_ f d fd fd:对于 I / O I/O I/O事件,它是文件描述符值;对于信号事件处理器,它是信号值

(4) e v ev ev_ b a s e base base:该事件处理器从属的 e v e n t event event_ b a s e base base实例

(5) _ e v ev ev:所有具有相同文件描述符值的 I / O I/O I/O事件或信号事件通过 e v ev ev. e v ev ev_ i o io io. e v ev ev_ i o io io_ n e x t next next e v ev ev. e v ev ev_ s i g n a l signal signal. e v ev ev_ s i g n a l signal signal_ n e x t next next串联成一个尾队列,称为I/O事件队列或信号事件队列。 e v ev ev. e v ev ev_ s i g n a l signal signal. e v ev ev_ n c a l l s ncalls ncalls指定信号事件发生时, R e a c t o r Reactor Reactor需要执行多少次该事件对应的事件处理器中的回调函数; e v ev ev. e v ev ev_ s i g n a l signal signal. e v ev ev_ p n c a l l s pncalls pncalls指针成员要么是 N U L L , 要 么 指 向 NULL,要么指向 NULLev . . .ev$_ s i g n a l signal signal. e v ev ev_ n c a l l s ncalls ncalls

(6) e v ev ev_ e v e n t s events events e v e n t event event关注的事件类型,可以是以下三种类型:

I/O事件: EV_WRITE和EV_READ 
定时事件:EV_TIMEOUT 
信号: EV_SIGNAL 
辅助选项:EV_PERSIST,表明是一个永久事件

L i b e v e n t Libevent Libevent定义如下,定义在 i n c l u d e / e v e n t 2 / e v e n t include/event2/event include/event2/event_ s t r u c t . h struct.h struct.h文件中( e v ev ev_ f l a g s flags flags):

#define EVLIST_TIMEOUT 0x01 //事件处理器从属于通用计时器队列或时间堆
#define EVLIST_INSERTED 0x02 //事件处理器从属于注册事件
#define EVLIST_SIGNAL 0x04 //未见使用
#define EVLIST_ACTIVE 0x08 //事件处理器从属于活动事件队列
#define EVLIST_INTERNAL 0x10 //内部使用
#define EVLIST_INIT 0x10 //事件处理器已经被初始化
#define EVLIST_ALL (0xf000 | 0x9f) //定义所有标志

事件类型使用"|"进行组合;信号事件与I/O事件不能同时被设置。

(7) e v ev ev_ r e s res res:记录当前激活事件的类型

(8) e v ev ev_ p r i pri pri:指定事件处理器优先级,值越小则优先级越高

(9) e v ev ev_ c l o s u r e closure closure:指定 e v e n t event event_ b a s e base base执行事件处理器的回调函数时的行为。其可选值定义于 e v e n t event event_ i n t e r n a l . h internal.h internal.h中:

//默认行为
#define EV_CLOSURE_NONE 0
//执行信号事件处理器的回调函数时,调用ev.ev_signal.ev_ncalls次该回调函数
#define EV_CLOSURE_SIGNAL 1
//执行完回调函数后,再次将事件处理器加入注册事件队列中
#define EV_CLOSURE_PERSIST 2

(10) e v ev ev_ t i m e o u t timeout timeout:仅对定时器有效,指定定时器的超时值

(11) e v ev ev_ c a l l b a c k callback callback:事件处理器的回调函数,由 e v e n t event event_ b a s e base base调用

(12) e v ev ev_ a r g arg arg:回调函数的参数

Libevent对于event的管理

  • e v e n t event event 结构体中的 3 个链表节点指针和一个堆索引出发,大体上也能窥出 l i b e v e n t 对 e v e n t libevent 对event libeventevent 的管理方法了,可以参见下面的示意图。
  • 每次当有事件 e v e n t event event 转变为就绪状态时, l i b e v e n t libevent libevent 就会把它移入到 a c t i v e active active e v e n t event event l i s t [ p r i o r i t y ] list[priority] list[priority]中,其中 p r i o r i t y priority priority e v e n t event event 的优先级;
  • 接着 l i b e v e n t libevent libevent 会根据自己的调度策略选择就绪事件,调用其 c b cb cb_ c a l l b a c k ( ) callback() callback()函数执行事件处理;并根据就绪的句柄和事件类型填充 c b cb cb_ c a l l b a c k callback callback 函数的参数。在这里插入图片描述

事件设置的接口函数

要向 l i b e v e n t libevent libevent 添加一个事件,需要首先设置 e v e n t event event 对象,这通过调用 l i b e v e n t libevent libevent 提供的函数有: e v e n t event event_ s e t ( ) set() set(), e v e n t event event_ b a s e base base_ s e t ( ) set() set(), e v e n t event event_ p r i o r i t y priority priority_ s e t ( ) set() set()来完成,定义在 e v e n t . c event.c event.c文件中:

(1) e v e n t event event_ s e t ( ) set() set()

1.设置事件 ev 绑定的文件描述符或者信号,对于定时事件,设为-1 即可;
2.设置事件类型,比如 EV_READ|EV_PERSIST, EV_WRITE, EV_SIGNAL 等;
3.设置事件的回调函数以及参数 arg;
4.初始化其它字段,比如缺省的 event_base 和优先级;
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; //设置event属于当前base

	ev->ev_callback = callback; //设置回调函数
	ev->ev_arg = arg; //设置回调函数的3个参数
	ev->ev_fd = fd;
	ev->ev_events = events;
	ev->ev_res = 0;
	ev->ev_flags = EVLIST_INIT; //设置event状态
	ev->ev_ncalls = 0;
	ev->ev_pncalls = NULL;

	if (events & EV_SIGNAL) {
		if ((events & (EV_READ|EV_WRITE|EV_CLOSED)) != 0) {
			event_warnx("%s: EV_SIGNAL is not compatible with "
			    "EV_READ, EV_WRITE or EV_CLOSED", __func__);
			return -1;
		}
		ev->ev_closure = EV_CLOSURE_EVENT_SIGNAL;
	} else {
		if (events & EV_PERSIST) {
			evutil_timerclear(&ev->ev_io_timeout);
			ev->ev_closure = EV_CLOSURE_EVENT_PERSIST;
		} else {
			ev->ev_closure = EV_CLOSURE_EVENT;
		}
	}

	min_heap_elem_init_(ev);  //初始化event在小根堆中索引为-1    min_heap.h
 
	if (base != NULL) {
		/* by default, we put new events into the middle priority */
		ev->ev_pri = base->nactivequeues / 2;  //设置event优先级
	}

	event_debug_note_setup_(ev);

	return 0;
}
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 == 0);
}

(2) e v e n t event event_ b a s e base base_ s e t ( ) set() set()

1.设置 event ev 将要注册到的 event_base; 
2.libevent 有一个全局 event_base 指针 current_base,默认情况下事件 ev
将被注册到 current_base 上,使用该函数可以指定不同的 event_base; 
3.如果一个进程中存在多个 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) //只能对新建的event设置event_base
		return (-1);

	event_debug_assert_is_setup_(ev);

	ev->ev_base = base;  //设置所属event_base
	ev->ev_pri = base->nactivequeues/2;  //设置event优先级

	return (0);
}

(3) e v e n t event event_ p r i o r i t y priority priority_ s e t ( ) set() set()

1.设置event ev的优先级
2.当ev正处于就绪状态时,不能设置,返回-1
int
event_priority_set(struct event *ev, int pri)
{
	event_debug_assert_is_setup_(ev);

	if (ev->ev_flags & EVLIST_ACTIVE)  //不能对活跃的event设置优先级
		return (-1);
	if (pri < 0 || pri >= ev->ev_base->nactivequeues)
		return (-1);

	ev->ev_pri = pri;  //设置优先级

	return (0);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值