libevent源码学习3---事件event

news2025/1/9 23:54:33

libevent源码学习3—事件event

libevent 的基本操作单元是事件。每个事件代表一组条件的集合, 这些条件包括:

  • 文件描述符已经就绪, 可以读取或者写入
  • 文件描述符变为就绪状态,可以读取或者写入(仅对于边沿触发 IO)
  • 超时事件
  • 发生某信号
  • 用户触发事件
    所有事件具有相似的生命周期。调用 libevent 函数设置事件并且关联到 event_base 之后, 事件进入“已初始化(initialized)”状态。此时可以将事件添加到 event_base 中,这使之进入“未决(pending)” 状态。在未决状态下, 如果触发事件的条件发生(比如说,文件描述符的状态改变, 或者超时时间到达 ),则事件进入“激活(active)”状态,(用户提供的)事件回调函数将被执行。如果配置为“持久的(persistent)”, 事件将保持为未决状态。否则, 执行完回调后, 事件不再是未决的。删除操作可以让未决事件成为非未决(已初始化)的 ; 添加操作可以让非未决事件再次成为未决的。

ps:未决: 简单来说就是一个已经产生的信号,但是还没有传递给任何进程,此时该信号的状态就称为未决状态。

1.创建事件

1.1 生成新事件

使用 event_new()接口创建事件。

#define EV_TIMEOUT      0x01
#define EV_READ         0x02
#define EV_WRITE        0x04
#define EV_SIGNAL       0x08
#define EV_PERSIST      0x10
#define EV_ET           0x20
typedef void (*event_callback_fn)(evutil_socket_t, short, void *);
struct event *event_new(struct event_base *base, evutil_socket_t fd,
    					short events, event_callback_fn cb, void *arg);
void event_free(struct event *event);

event_new()试图分配和构造一个用于 base 的新的事件。events参数是上述标志的集合。如果 fd 非负, 则它是将被观察其读写事件的文件。事件被激活时, libevent 将调用 cb 函数, 传递这些参数: 文件描述符 fd, 表示所有被触发事件的位字段 , 以及构造事件时的 arg 参数。发生内部错误, 或者传入无效参数时, event_new()将返回 NULL。

所有新创建的事件都处于已初始化和非未决状态 ,调用 event_add()可以使其成为未决的。

要释放事件, 调用 event_free()。对未决或者激活状态的事件调用 event_free()是安全的: 在释放事件之前, 函数将会使事件成为非激活和非未决的。

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) < 0)
    {
        mm_free(ev);
        return (NULL);
    }

    return (ev);
}

event_add()实现:

int event_add(struct event *ev, const struct timeval *tv)
{
    int res;

    if (EVUTIL_FAILURE_CHECK(!ev->ev_base))
    {
        event_warnx("%s: event has no event_base set.", __func__);
        return -1;
    }

    EVBASE_ACQUIRE_LOCK(ev->ev_base, th_base_lock);

    res = event_add_nolock_(ev, tv, 0);

    EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock);

    return (res);
}

event结构体定义:

struct event {
	struct event_callback ev_evcallback;

	/* 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 {
		/* used for io events */
		struct {
			LIST_ENTRY (event) ev_io_next;
			struct timeval ev_timeout;
		} ev_io;

		/* used by signal events */
		struct {
			LIST_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 */
	struct timeval ev_timeout;
};

1.2 事件标志

  • EV_TIMEOUT:这个标志表示某超时时间流逝后事件成为激活的。构造事件的时候, EV_TIMEOUT 标志是被忽略的: 可以在添加事件的时候设置超时, 也可以不设置。超时发生时,回调函数的 what 参数将带有这个标志。
  • EV_READ:表示指定的文件描述符已经就绪, 可以读取的时候, 事件将成为激活的。
  • EV_WRITE:表示指定的文件描述符已经就绪, 可以写入的时候, 事件将成为激活的。
  • EV_SIGNAL:用于实现信号检测
  • EV_PERSIST:表示事件是“持久的”
  • EV_ET:表示如果底层的 event_base 后端支持边沿触发事件(默认水平),则事件应该是边沿触发的。这个标志影响 EV_READ 和 EV_WRITE 的语义。

1.3 关于事件持久性

默认情况下,每当未决事件成为激活的(fd 已经准备好读取或者写入,或者因为超时),事件将在其回调被执行前成为非未决的。如果想让事件再次成为未决的,可以在回调函数中再次对其调用 event_add()。

然而,如果设置了 EV_PERSIST 标志,事件就是持久的。这意味着即使其回调被激活,事件还是会保持为未决状态。 如果想在回调中让事件成为非未决的,可以对其调用 event_del()。

每次执行事件回调的时候,持久事件的超时值会被复位。因此,如果具有 EV_READ|EV_PERSIST 标志,以及5秒(你设置的值)的超时值,则事件将在以下情况下成为激活的:

  • 套接字已经准备好被读取的时候
  • 从最后一次成为激活的开始,已经过去5秒

1.4 信号事件

libevent 也可以监测 POSIX 风格的信号。要构造信号处理器

#define evsignal_new(b, x, cb, arg)				\
	event_new((b), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg))

libevent 也提供了一组方便使用的宏用于处理信号事件:

#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_del(ev)		event_del(ev)
#define evsignal_pending(ev, tv)	event_pending((ev), EV_SIGNAL, (tv))
#define evsignal_initialized(ev)	event_initialized(ev)

2.事件的未决和非未决

构造事件之后,在将其添加到 event_base 之前实际上是不能对其做任何操作的。使用event_add()将事件添加到 event_base

2.1 设置未决事件

int event_add(struct event *ev, const struct timeval *tv);

在非未决的事件上调用 event_add()将使其在配置的 event_base 中成为未决的。成功时函数返回0, 失败时返回-1。

如果 tv 为 NULL, 添加的事件不会超时。否则, tv 以秒和微秒指定超时值。

如果对已经未决的事件调用 event_add(), 事件将保持未决状态, 并在指定的超时时间被重新调度。

2.2 设置非未决事件

int event_del(struct event *ev);

对已经初始化的事件调用 event_del()将使其成为非未决和非激活的。如果事件不是未决的或者激活的, 调用将没有效果。成功时函数返回 0, 失败时返回-1。

注意: 如果在事件激活后, 其回调被执行前删除事件, 回调将不会执行。

3.事件的优先级

int event_priority_set(struct event *ev, int pri)
{
    event_debug_assert_is_setup_(ev);

    if (ev->ev_flags & EVLIST_ACTIVE)
        return (-1);
    if (pri < 0 || pri >= ev->ev_base->nactivequeues)
        return (-1);

    ev->ev_pri = pri;

    return (0);
}

注意优先级的值的范围在第7行源码。相关数据结构释义:

/* Active event management. */
/** An array of nactivequeues queues for active 			
 event_callbacks (ones that have triggered, and whose callbacks need to be called). 
 Low priority numbers are more important, and stall higher ones.
*/
struct evcallback_list *activequeues;
/** The length of the activequeues array */
int nactivequeues;

4.检查事件状态

有时候需要了解事件是否已经添加,检查事件代表什么。libevent中有很多函数来获取不同的信息。

/**
  Checks if a specific event is pending or scheduled.

  @param ev an event struct previously passed to event_add()
  @param events the requested event type; any of EV_TIMEOUT|EV_READ|EV_WRITE|EV_SIGNAL
  @param tv if this field is not NULL, and the event has a timeout, this field is set to 
  		 hold the time at which the timeout will expire.
  @return true if the event is pending on any of the events in 'what', 
  		  (that is to say, it has been added), or 0 if the event is not added.
 */
int event_pending(const struct event *ev, short events, struct timeval *tv);

event_pending()函数确定给定的事件是否是未决的或者激活的。如果是,·而且 events 参数设置了 EV_READ、EV_WRITE、EV_SIGNAL 或者 EV_TIMEOUT 等标志,则函数会返回事件当前为之未决或者激活的所有标志 。

/**
   Extract _all_ of arguments given to construct a given event.  The event_base is copied into *base_out, 
   the fd is copied into *fd_out, and so on.

   If any of the "_out" arguments is NULL, it will be ignored.
 */
void event_get_assignment(const struct event *event, struct event_base **base_out, evutil_socket_t *fd_out, 
					      short *events_out, event_callback_fn *callback_out, void **arg_out);

event_get_assignment()复制所有为事件分配的字段到提供的指针中。任何为 NULL 的参数会被忽略。

当然还有很多获取状态的函数,详情去源码event.c中查看。

5.一次触发事件

如果不需要多次添加一个事件,或者要在添加后立即删除事件,而事件又不需要是持久的 , 则可以使用 event_base_once()

int event_base_once(struct event_base *, evutil_socket_t, short, event_callback_fn, void *, const struct timeval *);

除了不支持 EV_SIGNAL 或者 EV_PERSIST 之外,这个函数的接口与 event_new()相同。

6.手动激活事件

极少数情况下,需要在事件的条件没有触发的时候让事件成为激活的。

/* 
	You can use this function on a pending or a non-pending event to make it active, 
	so that its callback will be run by event_base_dispatch() or event_base_loop().

	One common use in multithreaded programs is to wake the thread running event_base_loop() from another thread.
	
 @param ev an event to make active.
 @param res a set of flags to pass to the event's callback.
 @param ncalls an obsolete argument: this is ignored.
*/
void event_active(struct event *ev, int res, short ncalls);

7.事件状态之间的转换

img

参考《libevent深入浅出》、libevent官方文档。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/923503.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++ string 类

文章目录 引用头文件初始化赋值1. 空串2. 拷贝复制3. 直接初始化赋值4. 单个字符初始化 遍历 string 类1. 下标索引遍历2. 迭代器遍历3. 使用 range for 循环遍历字符串&#xff08;需要 C11 或更新的版本&#xff09; string 常用方法判断字符串是否为空串获取字符串中字符个数…

Mysql简短又易懂

MySql 连接池:的两个参数 最大连接数&#xff1a;可以同时发起的最大连接数 单次最大数据报文&#xff1a;接受数据报文的最大长度 数据库如何存储数据 存储引擎&#xff1a; InnoDB:通过执行器对内存和磁盘的数据进行写入和读出 优化SQL语句innoDB会把需要写入或者更新的数…

FFI绕过disable_functions

文章目录 FFI绕过disable_functions[RCTF 2019]NextphpPHP7.4 FFI参考 FFI绕过disable_functions [RCTF 2019]Nextphp 首先来看这道题目 index.php <?php if (isset($_GET[a])) {eval($_GET[a]); } else {show_source(__FILE__); }查看一下phpinfo 发现过滤了很多函数&…

如何高效管理生产物料?

导 读 ( 文/ 2358 ) 所谓巧妇难为无米之炊&#xff0c;物料是生产过程的必需品&#xff0c;确保物料及时到料尤为关键&#xff0c;但堆积过多的物料库存将导致不必要的成本挤压。做好生产物料管理是门技术活&#xff0c;它的目标是确保所需的物料按时提供给生产线&#xff0c;以…

嘉立创EDA设计学习笔记1:开发环境下载与熟悉

今日开始尝试自己学习绘制PCB开发板&#xff0c;使得以后的学习小项目制作的成品不再受外形大小与他人布线的限制&#xff0c;用的软件是嘉立创EDA设计软件&#xff0c;这样直接布线检查与下单&#xff0c;十分方便。 嘉立创EDA设计软件下载&#xff1a; 首先上网址&#xff0…

【IMX6ULL驱动开发学习】09.Linux之I2C框架简介和驱动程序模板

参考&#xff1a;Linux之I2C驱动_linux i2c驱动_风间琉璃•的博客-CSDN博客​​​​​​ 目录 一、I2C驱动框架简介 1.1 I2C总线驱动 1.2 I2C设备驱动 二、I2C总线-设备-驱动模型 2.1 i2c_driver 2.2 i2c_client 2.3 I2C 设备数据收发和处理 三、Linux I2C驱动程序模板…

redis常用五种数据类型详解

目录 前言&#xff1a; string 相关命令 内部编码 应用场景 hash 相关命令 内部编码 应用场景 list 相关命令 内部编码 应用场景 set 相关命令 内部编码 应用场景 Zset 相关命令 内部编码 应用场景 渐进式遍历 前言&#xff1a; redis有多种数据类型&…

Java实现一个简单的图书管理系统(内有源码)

简介 哈喽哈喽大家好啊&#xff0c;之前作者也是讲了Java不少的知识点了&#xff0c;为了巩固之前的知识点再为了让我们深入Java面向对象这一基本特性&#xff0c;就让我们完成一个图书管理系统的小项目吧。 项目简介&#xff1a;通过管理员和普通用户的两种操作界面&#xff0…

【网络安全】防火墙知识点全面图解(三)

本系列文章包含&#xff1a; 【网络安全】防火墙知识点全面图解&#xff08;一&#xff09;【网络安全】防火墙知识点全面图解&#xff08;二&#xff09;【网络安全】防火墙知识点全面图解&#xff08;三&#xff09; 防火墙知识点全面图解&#xff08;三&#xff09; 39、什…

生成式人工智能的潜在有害影响与未来之路(二)

利润高于隐私&#xff1a;不透明数据收集增加 背景和风险 生成型人工智能工具建立在各种大型、复杂的机器学习模型之上&#xff0c;这些模型需要大量的训练数据才能发挥作用。对于像ChatGPT这样的工具&#xff0c;数据包括从互联网上抓取的文本。对于像Lensa或Stable Diffusi…

C语言练习1(巩固提升)

C语言练习1 选择题 前言 “人生在勤&#xff0c;勤则不匮。”幸福不会从天降&#xff0c;美好生活靠劳动创造。全面建成小康社会的奋斗目标&#xff0c;为广大劳动群众指明了光明的未来&#xff1b;全面建成小康社会的历史任务&#xff0c;为广大劳动群众赋予了光荣的使命&…

让你对es有一个初步的了解

首先es在海量数据的搜索能力非常好&#xff0c;es你可以把他看成一个搜索引擎数据库&#xff0c;他是个非关系型数据库。他的语法有很大的不同&#xff0c;好像都是json风格的。还有一点需要说的就是es 的数据是存在硬盘上的&#xff0c; 我们先来看一下mysql和es的区别吧。一…

边写代码边学习之Bidirectional LSTM

1. 什么是Bidirectional LSTM 双向 LSTM (BiLSTM) 是一种主要用于自然语言处理的循环神经网络。 与标准 LSTM 不同&#xff0c;输入是双向流动的&#xff0c;并且它能够利用双方的信息。 它也是一个强大的工具&#xff0c;可以在序列的两个方向上对单词和短语之间的顺序依赖…

4H-SiC UMOSFET 结构中带有 p+-polySi/SiC 被屏蔽区的模拟研究

标题&#xff1a;Simulation Study of 4H-SiC UMOSFET Structure With p -polySi/SiC Shielded Region 摘要 在这篇论文中&#xff0c;我们提出了一种改进效率的4H-SiC U型沟槽栅MOSFET&#xff08;UMOSFET&#xff09;结构。所提出的器件结构利用ppolySi/SiC被屏蔽区来降低导…

学习Linux的注意事项(使用经验;目录作用;服务器注意事项)

本篇分享学习Linux过程中的一些经验 文章目录 1. Linux系统的使用经验2. Linux各目录的作用3. 服务器注意事项 1. Linux系统的使用经验 Linux严格区分大小写Linux中所有内容以文件形式保存&#xff0c;包括硬件&#xff0c;Linux是以管理文件的方式操作硬件 硬盘文件是/dev/s…

mybatis概述及搭建

目录 1.概述 2.mybatis搭建 1.创建一个maven项目&#xff0c;添加mybatis、mysql所依赖的jar 2.创建一个数据库表&#xff0c;及对应的java类 3.创建一个mybatis的核心配置文件&#xff0c;配置数据库连接信息&#xff0c;配置sql映射文件 4.创建sql映射文件&#xff0c;…

Matlab高光谱遥感数据处理与混合像元分解实践技术

光谱和图像是人们观察世界的两种方式&#xff0c;高光谱遥感通过“图谱合一”的技术创新将两者结合起来&#xff0c;大大提高了人们对客观世界的认知能力&#xff0c;本来在宽波段遥感中不可探测的物质&#xff0c;在高光谱遥感中能被探测。以高光谱遥感为核心&#xff0c;构建…

Mysql报错 mysqladmin flush-hosts

出现这个的原因是错误连接达到数据库设置的最大值。 此时需要释放重置连接最大值。 进入mysql使用命令 flush-hosts;环境说明&#xff1a; 内网测试服务器192.168.18.251 为WEB服务器&#xff0c;安装了mysql; 内网音视频转码服务器192.168.18.253安装了转码工具&#xff0…

IP协议报文结构

IP报文结构 4位版本号: 指定IP协议的版本, 对于IPv4来说, 就是4.4位头部长度: IP头部的长度是多少个32bit, 也就是 length * 4 的字节数. 4bit表示最大的数字是15, 因此IP头部最大长度是60字节.8位服务类型: 3位优先权字段(已经弃用) 4位TOS字段 1位保留字段(必须置为0). 4位…

【使用perf和火焰图分析PostgreSQL数据库的性能瓶颈】

Perf工具可用来对软件进行优化&#xff0c;包括算法优化&#xff08;空间复杂度、时间复杂度&#xff09;和代码优化&#xff08;提高执行速度、减少内存占用&#xff09;等等&#xff0c;perf 最常用的参数有top、stat、record&#xff0c;另外还有list和report等。 本文主要使…