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

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.rhkb.cn/news/103908.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CentOS ens160 显示disconnected

使用nmcli device查看网卡状态&#xff0c;显示如图&#xff1a; 检查宿主机系统VMware DHCP Sevice和VMware NAT Sevice服务是否正常运行。 右键点击我的电脑管理按钮&#xff0c;打开计算机管理点击服务

elementui的el-tabs标签页样式修改

一、官网样式&#xff1a; 二、修改样式 1.去掉下划线 效果&#xff1a; 代码: /* 去掉tabs标签栏下的下划线 */ ::v-deep .el-tabs__nav-wrap::after {position: static !important;/* background-color: #fff; */ } 2.改变下划线颜色 效果&#xff1a; 代码&#xff1a;…

JVM——垃圾回收(垃圾回收算法+分代垃圾回收+垃圾回收器)

1.如何判断对象可以回收 1.1引用计数法 只要一个对象被其他对象所引用&#xff0c;就要让该对象的技术加1&#xff0c;某个对象不再引用其&#xff0c;则让它计数减1。当计数变为0时就可以作为垃圾被回收。 有一个弊端叫做循环引用&#xff0c;两个的引用计数都是1&#xff…

STM32 printf函数

printf函数输出流程 用户调用printf()函数到C标准库调用printf函数相关部分&#xff0c;printf函数由编译器提供的stdio.h解析。包含在usart.h文件中。fputc()最终实现输出。用户需要根据最终输出的硬件重新定义该函数&#xff0c;此过程为&#xff1a;printf重定向。 printf的…

测试框架pytest教程(8)失败重试-pytest-rerunfailures

pytest-rerunfailures是一个pytest插件&#xff0c;用于重新运行失败的测试用例。当测试用例在第一次运行时失败&#xff0c;该插件会自动重新运行指定次数的失败用例&#xff0c;以提高稳定性和减少偶发性错误的影响。 要使用pytest-rerunfailures插件&#xff0c;需要按照以…

MySQL双主架构、主从架构

为什么要对数据库做优化&#xff1f; MySQL官方说法&#xff1a; 单表2000万数据就达到瓶颈了。所以为了保证查询效率&#xff0c;要让每张表的大小得到控制。 MySQL主主架构 主数据库都负责增删改查。 比如有1000W的数据&#xff0c;有两个主数据库&#xff0c;就将数据分流给…

学习网络编程No.4【socket编程实战】

引言 北京时间&#xff1a;2023/8/19/23:01&#xff0c;耍了好几天&#xff0c;主要归咎于《我欲封天》这本小说&#xff0c;听了几个晚上之后逐渐入门&#xff0c;在闲暇时间又看了一下&#xff0c;小高潮直接来临&#xff0c;最终在三个昼夜下追完了&#xff0c;哈哈哈&…

在 Pytorch 中使用 TensorBoard

机器学习的训练过程中会产生各类数据&#xff0c;包括 “标量scalar”、“图像image”、“统计图diagram”、“视频video”、“音频audio”、“文本text”、“嵌入Embedding” 等等。为了更好地追踪和分析这些数据&#xff0c;许多可视化工具应运而生&#xff0c;比如之前介绍的…

使用 ChatGPT 创建 PowerPoint 演示文稿

让 ChatGPT 成为您的助手来帮助您编写电子邮件很简单,因为众所周知,它非常能够生成文本。很明显,ChatGPT 无法帮助您做饭。但您可能想知道它是否可以生成文本以外的其他内容。在上一篇文章中,您了解到 ChatGPT 只能通过中间语言为您生成图形。在这篇文章中,您将了解使用中…

Ubuntu 配置国内源

配置国内源 因为众所周知的原因&#xff0c;国外的很多网站在国内是访问不了或者访问极慢的&#xff0c;这其中就包括了Ubuntu的官方源。 所以&#xff0c;想要流畅的使用apt安装应用&#xff0c;就需要配置国内源的镜像。 市面上Ubuntu的国内镜像源非常多&#xff0c;比较有…

PyTorch训练简单的生成对抗网络GAN

文章目录 原理代码结果参考 原理 同时训练两个网络&#xff1a;辨别器Discriminator 和 生成器Generator Generator是 造假者&#xff0c;用来生成假数据。 Discriminator 是警察&#xff0c;尽可能的分辨出来哪些是造假的&#xff0c;哪些是真实的数据。 目的&#xff1a;使…

小区新冠疫情管理系统的设计与实现/基于springboot的小区疫情管理系统

摘要 采用更加便于维护和使用的Java语言&#xff0c;其可拓展性高且更富于表现力&#xff0c;基于mysql数据库、Springboot框架开发的小区新冠疫情管理系统&#xff0c;方便用户查看物资信息、疫苗信息。通过Eclipse来进行网页编程&#xff0c;其方便易用、移植适用性广、更加安…

日志搞不定?手把手教你如何使用Log4j2

系列文章目录 从零开始&#xff0c;手把手教你搭建Spring Boot后台工程并说明 Spring框架与SpringBoot的关联与区别 SpringBean生成流程详解 —— 由浅入深(附超精细流程图) Spring监听器用法与原理详解 Spring事务畅谈 —— 由浅入深彻底弄懂 Transactional注解 面试热点详解…

装备制造企业如何执行精益管理?

导 读 ( 文/ 2358 ) 精益管理是一种以提高效率、降低成本和优化流程为目标的管理方法。装备制造行业具备人工参与度高&#xff0c;产成品价值高&#xff0c;质量要求高的特点。 在装备制造企业中实施精益管理可以帮助企业提高竞争力、提升生产效率并提供高质量的产品。本文将…

边缘计算节点BEC典型实践:如何快速上手PC-Farm服务器?

百度智能云边缘计算节点BEC&#xff08;Baidu Edge Computing&#xff09;基于运营商边缘节点和网络构建&#xff0c;一站式提供靠近终端用户的弹性计算资源。边缘计算节点在海外覆盖五大洲&#xff0c;在国内覆盖全国七大区、三大运营商。BEC通过就近计算和处理&#xff0c;大…

【算法日志】贪心算法刷题:单调递增数列,贪心算法总结(day32)

代码随想录刷题60Day 目录 前言 单调递增数列 贪心算法总结 前言 今天是贪心算法刷题的最后一天&#xff0c;今天本来是打算刷两道题&#xff0c;其中的一道hard题做了好久都没有做出来(主要思路错了)。然后再总结一下。 单调递增数列 int monotoneIncreasingDigits(int n…

Wlan——STA上线流程与802.11MAC帧讲解以及报文转发路径

目录 802.11MAC帧基本概念 802.11帧结构 802.11MAC帧的分类 管理帧 控制帧 数据帧 STA接入无线网络流程 信号扫描—管理帧 链路认证—管理帧 用户关联—管理帧 用户上线 不同802.11帧的转发路径 802.11MAC帧基本概念 802.11协议在802家族中的角色位置 其中802.3标…

Linux内核学习(八)—— 内存管理(基于Linux 2.6内核)

目录 一、页&#xff08;page&#xff09; 二、区&#xff08;zone&#xff09; 三、页操作 四、kmalloc() 五、vmalloc() 六、slab 分配器 七、在栈上的静态分配 一、页&#xff08;page&#xff09; 内核把物理页作为内存管理的基本单位。尽管处理器的最小可寻 …

网工内推 | 锐捷招云工程师,HCIE、CCIE、RHCE优先,25k*13薪

01 锐捷网络 招聘岗位&#xff1a;云方案工程师 职责描述&#xff1a; 1、负责云数据中心方案项目方案设计撰写、项目实施交付、故障处理、业务割接、客户培训、现场保障、网络优化、网络巡检等技术相关业务 2、负责云数据中心方案新技术文档沉淀、体系建设、工具开发等标准化…

使用element-plus组件,默认显示英文 转换为中文

最近在边写项目边学习vue3 所以这几天没有更新 找机会把vue3的知识也统计一下吧 先说今天遇到的问题 最近做项目的时候使用element-plus分页组件时发现&#xff0c;显示的不是中文的了&#xff0c;是英文的 解决方法 在app.vue里面配置 <template><el-config-provi…