事件处理框架

news/2024/4/23 4:06:27/

一、事件处理框架-event_base

Reactor模式有几个基础的组件,其中的Reactor框架就是event_base结构体,下面是定义(只列出部分常用的成员),如下:

struct event_base {/* 后台使用的I/O多路复用机制 */const struct eventop *evsel;/* 指向struct epollop对象的指针 */void *evbase;/*** 激活队列(数组)用来保存激活事件对应的回调函数。每个事件有一个event_callback类型的成员ev_evcallback,用来保存事件对应的回调* 当事件被触发时,会把事件的回调保存到激活队列。激活队列是一个优先级队列,下标越小优先级越高。*/struct evcallback_list *activequeues;/* activequeues 数组的长度 */int nactivequeues;/* 公共超时队列 */struct common_timeout_list **common_timeout_queues;/* 公共超时队列的实际长度 */int n_common_timeouts;/* 公共超时队列的容量 */int n_common_timeouts_allocated;/* 保存注册的I/O事件 */struct event_io_map io;/* 保存注册的信号事件 */struct event_signal_map sigmap;/* 这是一个最小堆,用来保存注册的超时事件 */struct min_heap timeheap;
};

二,创建和初始化event_base

1、创建默认的event_base

创建一个 event_base 对象也既是创建了一个新的 libevent 实例,event_base_new()函数分配并且返回一个新的具有默认设置的 event_base。一般情况下整个默认的event_base 对象就能满足需求。

2、创建复杂的event_base

如果要创建复杂的event_base 对象就需要使用 event_config,下面是一些常用的接口

struct event_config *event_config_new(void);
struct event_base *event_base_new_with_config(const struct event_config *cfg);
void event_config_free(struct event_config *cfg);

先调用 event_config_new()分配一个event_config。 然后,对event_config调用其它函数,设置所需要的event_base特征。最后,调用 event_base_new_with_config()创建新的 event_base。完成创建工作后,使用event_config_free()释放event_config。下面是设置event_base特征的接口

接口:int event_config_avoid_method(struct event_config *cfg, const char *method);
功能:让libevent避免使用特定的I/O多路复用机制
示例:
/* 不使用select与poll机制 */
event_config_avoid_method(cfg, "select");
event_config_avoid_method(cfg, "poll");接口:int event_config_require_features(struct event_config *cfg, enum event_method_feature feature);
功能:让libevent使用所指定特征的I/O多路复用机制
示例:
enum event_method_feature {/* 要求支持边沿触发的I/O多路复用机制 */EV_FEATURE_ET = 0x01,/* 要求添加、删除单个事件或者确定哪个事件激活的操作是 O(1)复杂度的I/O多路复用机制 */EV_FEATURE_O1 = 0x02,/* 要求支持任意文件描述符,而不仅仅是套接字的I/O多路复用机制 */EV_FEATURE_FDS = 0x04,
};接口:int event_config_require_features(struct event_config *cfg, enum event_method_feature feature);
功能:libevent 在创建 event_base 时设置一个或者多个运行时标志
示例:
enum event_base_config_flag {/* 不要为 event_base 分配锁,设置这个选项可以 为 event_base 节省一点用于锁定和解锁的时间,但是让在多个线程中访问 event_base 成为不安全的 */EVENT_BASE_FLAG_NOLOCK = 0x01,EVENT_BASE_FLAG_IGNORE_ENV = 0x02,EVENT_BASE_FLAG_STARTUP_IOCP = 0x04,EVENT_BASE_FLAG_NO_CACHE_TIME = 0x08,EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST = 0x10,EVENT_BASE_FLAG_PRECISE_TIMER = 0x20
};

三,接口函数

Reactor 框架的作用就是提供事件的注册、注销接口;根据系统提供的事件多路分发机制执行事件循环,当有事件进入“就绪”状态时,调用注册事件的回调函数来处理事件。Libevent 中对应的接口函数主要就是:

int event_add(struct event *ev, const struct timeval *timeout); 
int event_del(struct event *ev); 
int event_base_loop(struct event_base *base, int loops); 
void event_active(struct event *event, int res, short events); 
void event_process_active(struct event_base *base);

1、事件注册

函数原型:int event_add(struct event *ev, const struct timeval *tv)
参数:
ev:指向要注册的事件
tv:超时时间
函数功能:函数将 ev 注册到 ev->ev_base 上,事件类型由 ev->ev_events 指明,如果注册成功,ev将被插入到已注册链表中;如果 tv 不是 NULL,则会同时注册定时事件,将 ev 添加到 timer堆上。下面是具体的代码实现,如下:

/* ****************************************************************************函 数 名  : event_add_nolock_功能描述  : 注册事件输入参数  : ev    : 要注册的事件tv    : 超时时间tv_is_absolute    : 用来判断传入的时间是绝对时间还是相对时间,1:绝对时间;0:相对时间(即:是一个时长,例如:2s)返 回 值  : 成功 0, 失败 -1
**************************************************************************** */
int event_add_nolock_(struct event *ev, const struct timeval *tv,int tv_is_absolute)
{struct event_base *base = ev->ev_base;int res = 0;int notify = 0;/* 如果event设置了超时时间,并且event所设超时结构体不在time小根堆上,则在time小根堆中预留空间 */if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) {if (min_heap_reserve_(&base->timeheap,1 + min_heap_size_(&base->timeheap)) == -1)return (-1);  /* ENOMEM == errno */}/* 如果是I/O事件或信号事件,并且事件ev不在已注册或者激活链表中 */if ((ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED|EV_SIGNAL)) &&!(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE|EVLIST_ACTIVE_LATER))) {/* 如果是I/O事件,把事件添加到I/O链表 */if (ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED))res = evmap_io_add_(base, ev->ev_fd, ev);/* 如果是信号事件,把事件添加到信号链表 */else if (ev->ev_events & EV_SIGNAL)res = evmap_signal_add_(base, (int)ev->ev_fd, ev);if (res != -1)event_queue_insert_inserted(base, ev);/* res == 1 说明监控的事件有变更 */if (res == 1) {/* 需要通知main thread,即:运行事件循环的线程,也就是调用event_base_loop的线程 */notify = 1;res = 0;}}/* 准备添加定时事件 */if (res != -1 && tv != NULL) {struct timeval now;int common_timeout;
#ifdef USE_REINSERT_TIMEOUTint was_common;int old_timeout_idx;
#endif/* 如果业务侧把这个事件设置成EV_PERSIST(即:永久事件),并且是相对超时时间(即:设置的是一个超时时长) */if (ev->ev_closure == EV_CLOSURE_EVENT_PERSIST && !tv_is_absolute)/* 记录业务侧设置的超时时长 */ev->ev_io_timeout = *tv;#ifndef USE_REINSERT_TIMEOUTif (ev->ev_flags & EVLIST_TIMEOUT) {event_queue_remove_timeout(base, ev);}
#endifgettime(base, &now);common_timeout = is_common_timeout(tv, base);
#ifdef USE_REINSERT_TIMEOUTwas_common = is_common_timeout(&ev->ev_timeout, base);old_timeout_idx = COMMON_TIMEOUT_IDX(&ev->ev_timeout);
#endif/* 如果是绝对时间 */if (tv_is_absolute) {ev->ev_timeout = *tv;} else if (common_timeout) {/* 超时时间在公共队列 */struct timeval tmp = *tv;tmp.tv_usec &= MICROSECONDS_MASK;evutil_timeradd(&now, &tmp, &ev->ev_timeout);/* 公共超时时间带上标志 */ev->ev_timeout.tv_usec |= (tv->tv_usec & ~MICROSECONDS_MASK);} else { /* 设置的是一个超时时长,并且超时时长不是common-timeout *//* now表示的是当前时间,tv是超时时间,ev->ev_timeout保存的就是超时后的时间 */evutil_timeradd(&now, tv, &ev->ev_timeout);}#ifdef USE_REINSERT_TIMEOUTevent_queue_reinsert_timeout(base, ev, was_common, common_timeout, old_timeout_idx);
#else/* 将超时事件插入到超时队列(common-timeout 队列或小根堆) */event_queue_insert_timeout(base, ev);
#endif/* 如果是一个公共超时时间 */if (common_timeout) {struct common_timeout_list *ctl =get_common_timeout_list(base, &ev->ev_timeout);/* 如果插入的事件是队列的第一个事件,注册一个超时事件 */if (ev == TAILQ_FIRST(&ctl->events)) {common_timeout_schedule(ctl, &now, ev);}} else {struct event* top = NULL;if (min_heap_elt_is_top_(ev))/* 如果本次插入的是最小的,就需要唤醒主线程,告诉其最小超时值已经变了 */notify = 1;/* 如果小根堆堆顶的节点(即:拥有最小超时时间的节点)对应的超时时间小于当前时间,说明当前堆顶的节点已经超时 */else if ((top = min_heap_top_(&base->timeheap)) != NULL &&evutil_timercmp(&top->ev_timeout, &now, <))notify = 1;}}/* 如果当前线程不是主线程且注册事件成功,就唤醒主线程 */if (res != -1 && notify && EVBASE_NEED_NOTIFY(base))evthread_notify_base(base);event_debug_note_add_(ev);return (res);
}

2、事件删除

函数原型:int event_del(struct event *ev);
参数:
ev:要删除的事件
函数功能:该函数将删除事件 ev,对于 I/O 事件,从 I/O 的 demultiplexer 上将事件注销;对于 Signal事件,将从 Signal 事件链表中删除;对于定时事件,将从堆上删除。下面是具体的代码实现,如下:

int event_del_nolock_(struct event *ev, int blocking)
{struct event_base *base;int res = 0, notify = 0;/* An event without a base has not been added */if (ev->ev_base == NULL)return (-1);base = ev->ev_base;EVUTIL_ASSERT(!(ev->ev_flags & ~EVLIST_ALL));/* 如果事件在超时队列中 */if (ev->ev_flags & EVLIST_TIMEOUT) {event_queue_remove_timeout(base, ev);}/* 如果事件已经激活 */if (ev->ev_flags & EVLIST_ACTIVE)event_queue_remove_active(base, event_to_event_callback(ev));else if (ev->ev_flags & EVLIST_ACTIVE_LATER)event_queue_remove_active_later(base, event_to_event_callback(ev));/* 如果事件已经添加到队列 */if (ev->ev_flags & EVLIST_INSERTED) {event_queue_remove_inserted(base, ev);if (ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED))res = evmap_io_del_(base, ev->ev_fd, ev);elseres = evmap_signal_del_(base, (int)ev->ev_fd, ev);if (res == 1) {/* evmap says we need to notify the main thread. */notify = 1;res = 0;}if (!event_haveevents(base) && !N_ACTIVE_CALLBACKS(base))notify = 1;}/* if we are not in the right thread, we need to wake up the loop */if (res != -1 && notify && EVBASE_NEED_NOTIFY(base))evthread_notify_base(base);event_debug_note_del_(ev);return (res);
}

http://www.ppmy.cn/news/48177.html

相关文章

分享一些提升效率的小工具

1、 IObit Uninstaller IObit Uninstaller是一款简单专业的卸载工具&#xff0c;可以帮我们卸载电脑中顽固难卸的软件和浏览器插件&#xff0c;支持强制一键卸载和文件粉碎功能。 除了卸载软件&#xff0c;它还可以自动帮我们检测软件安装、检测软件更新、查看工具栏和插件。 …

K8S使用持久化卷存储到NFS(NAS盘)

参考文章&#xff1a;K8S-v1.20中使用PVC持久卷 - 知乎 目录 1、概念&#xff1a; 1.1 基础概念 1.2 PV的配置 1.2.1 静态PV配置 1.2.2 动态PV配置 1.2.3 PVC与PV的绑定 1.2.4 PVC及PV的使用 2 部署PV及PVC 2.1 所有K8S机器都需要安装NFS程序 2.2 仅针对需要暴露文件…

yara规则--编写

编写 YARA 规则 — yara 4.2.0 文档 YARA规则易于编写和理解&#xff0c;并且它们的语法是 类似于 C 语言。这是您可以编写的最简单的规则 YARA&#xff0c;它什么都不做&#xff1a; rule dummy {condition:false } 一、规则标识符 每个规则都以关键字“ rule”开头&#xff0…

如何处理嵌入式中程序错误

一、错误概念 1.1 错误分类 从严重性而言&#xff0c;程序错误可分为致命性和非致命性两类。对于致命性错误&#xff0c;无法执行恢复动作&#xff0c;最多只能在用户屏幕上打印出错消息或将其写入日志文件&#xff0c;然后终止程序&#xff1b;而对于非致命性错误&#xff0…

近期遇到的vscode 插件开发的问题,when表达式,正则匹配路径

前言 最近在修改dbt插件的时候&#xff0c;遇到了几个比较奇怪的问题&#xff0c;猜测应该是因为跟新了VSCode导致的。 这里稍微记录一下&#xff0c;如果能帮助其他的同学&#xff0c;那就太值啦。 when 正则表达式 之前有在packgae.json 中使用 when 的正则表达式&#xf…

flask-restful 和 blueprint

flask-restful 使用 flask-restful第三方库 from flask_restful import Resource, Api app Flask(__name__) api Api(app) class HelloWorldResource(Resource): def get(self): return {hello:world} def post(self): return {msg&#xff1a;‘post hello wolrd’} api…

非关系型数据库---Redis安装与基本使用

一、数据库类型 关系数据库管理系统(RDBMS)非关系数据库管理系统(NoSQL) 按照预先设置的组织机构&#xff0c;将数据存储在物理介质上(即&#xff1a;硬盘上) 数据之间可以做无关联操作 (例如: 多表查询&#xff0c;嵌套查询&#xff0c;外键等) 主流的RDBMS软件&#xff1a;My…

查询练习:查询所有学生的 s_no 、c_no 和 grade 列。

思路是&#xff0c;使用区间 ( BETWEEN ) 查询&#xff0c;判断学生的成绩 ( degree ) 在 grade 表的 low 和 upp 之间。 SELECT s_no, c_no, grade FROM score, grade WHERE degree BETWEEN low AND upp; -------------------- | s_no | c_no | grade | ------------------…

FPGA基于SFP光口实现10G万兆网UDP通信 10G Ethernet Subsystem替代网络PHY芯片 提供工程源码和技术支持

目录 1、前言2、我这里已有的UDP方案3、详细设计方案4、vivado工程详解5、上板调试验证并演示6、福利&#xff1a;工程代码的获取 1、前言 目前网上的fpga实现udp基本生态如下&#xff1a; 1&#xff1a;verilog编写的udp收发器&#xff0c;但不带ping功能&#xff0c;这样的代…

Ruby语言发展史(个人理解)

Ruby是一种动态、面向对象、通用的编程语言&#xff0c;它由松本行弘&#xff08;Yukihiro Matsumoto&#xff09;创造&#xff0c;最初于1995年发布。Ruby的设计目标是“人性化”&#xff0c;即让编程更加简单、自然和可读性强。Ruby的开发历程可以分为以下几个阶段。 1. Rub…

Python-DQN代码阅读(12)

目录 1.代码 1.1代码解读 1.2 代码分解 1.2.1 latest_checkpoint tf.train.latest_checkpoint(checkpoint_dir) 1.2.2 saver.restore(sess, latest_checkpoint) 1.2.3 sess.run(tf.global_variables_initializer()) 1.2.4 deep_q_learning() 1.3 输出结果 1.4 问题 1…

如何搭建自己的博客网站(手把手教你搭建免费个人博客网站)

没有前言直接开始正文&#xff0c;搭建一个博客需要服务器&#xff0c;域名&#xff0c;博客程序。 博客程序常用的有wordpress&#xff0c;z-blog&#xff0c;typecho等等&#xff0c;其中wordpress和z-blog最为简单&#xff0c;typecho需要一定的技术含量&#xff0c;这里暂…

【状态估计】用于描述符 LTI 和 LPV 系统的分析、状态估计和故障检测的算法(Matlab代码实现)

&#x1f4a5; &#x1f4a5; &#x1f49e; &#x1f49e; 欢迎来到本博客 ❤️ ❤️ &#x1f4a5; &#x1f4a5; &#x1f3c6; 博主优势&#xff1a; &#x1f31e; &#x1f31e; &#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 …

3、公司法规 - 开公司创业系列文章

法律内容 第一章 总 则 第一条 为了规范公司的组织和行为&#xff0c;保护公司、股东和债权人的合法权益&#xff0c;维护社会经济秩序&#xff0c;促进社会主义市场经济的发展&#xff0c;制定本法。 第二条 本法所称公司是指依照本法在中国境内设立的有限责任公司和股份有限…

ijkplayer编译支持x264编码器

ijkplayer作为一款基于ffmpeg在移动端比较流行的播放器&#xff0c;并没有集成编码器。如果使用ffmpeg且需要音视频编码功能的话就需要自己手动集成。 编译支持x264编码器的流程主要参考了 ijkplayer支持x264编译 - 简书 原理与ijkplayer编译支持openssl的流程类似。 1 下载x…

SSeg总体思路

1&#xff1a;在train中加载train和test数据集。 2&#xff1a;指定数据集为kitti&#xff0c;确定训练验证的batchsize。 3&#xff1a;提前定义好数据预处理&#xff0c;首先是几何变换&#xff0c;包括随机裁切等。 4&#xff1a;然后是外观变换&#xff0c;包括高斯滤波…

标准C库函数之memcpy(内存函数)

memcpy函数 memcpy函数是C标准库&#xff08;string.h库&#xff09;中一个非常重要且常用的函数。它用于将一段内存区域的内容复制到另一个内存区域。这个函数在底层使用内存操作进行数据复制&#xff0c;通常比逐个元素的复制更高效。 memcpy函数的原型如下&#xff1a; #in…

PCB阻焊桥存在的DFM(可制造性)问题,华秋一文告诉你

PCB表面的一层漆&#xff0c;称为阻焊油墨&#xff0c;也就是PCB线路板阻焊油墨。阻焊油墨是PCB线路板中非常常见、也是主要使用的油墨&#xff0c;一般90%都是绿色&#xff0c;但也有杂色油墨&#xff1a;红色、蓝色、黑色、白色、黄色等。 阻焊油墨的作用就是绝缘&#xff0…

AI绘画——ControlNet扩展安装教程

目录 1.ControlNet安装 2.预处理模型安装 预处理模型&#xff08;annotator&#xff09;下载链接&#xff1a; 预处理模型安装地址&#xff1a; 3.ControlNet模型下载 Controlnet模型下载地址&#xff1a; Controlnet模型安装目录&#xff1a; 注&#xff1a;&…

English Learning - L2 语音作业打卡 语音语调四步法 Day59 2023.4.20 周四

English Learning - L2 语音作业打卡 语调四步法 Day59 2023.4.20 周四 &#x1f48c;发音小贴士&#xff1a;&#x1f48c;当日目标音发音规则/技巧:&#x1f36d; Part 1【热身练习】&#x1f36d; Part2【练习内容】&#x1f36d;【练习感受】&#x1f36d; Part3【存在问题…