vps网站目录是灰色的西安做推广网站设计
news/
2025/9/23 21:42:22/
文章来源:
vps网站目录是灰色的,西安做推广网站设计,展厅设计图片,网站后台上传文章为什么不显示概述
libevent的event#xff0c;event_callback#xff0c;event_base除了可以用来支持套接字的自动和手动分发#xff0c;也可用来支持定时机制#xff0c;信号处理#xff0e;这里#xff0c;我们补充对定时机制#xff0c;信号处理的分析#xff0e;
libevent中的…概述
libevent的eventevent_callbackevent_base除了可以用来支持套接字的自动和手动分发也可用来支持定时机制信号处理这里我们补充对定时机制信号处理的分析
libevent中的通信对象集成了对流量控制的支持我们这里补充对通信对象流量控制的分析
libevent中的定时机制
1.概述 前面我们分析eventevent_callbackevent_base时立足于服务于套接字的event来展开分析的 libevent中的event既可以用于支持对套接字的自动分法手动分发也可用于支持定时机制还可用于支持信号处理
这一部分立足于支持定时机制来分析libevent中eventevent_callbackevent_base 2. event对于定时支持
struct event {struct event_callback ev_evcallback;union {TAILQ_ENTRY(event) ev_next_with_common_timeout;size_t min_heap_idx;} ev_timeout_pos;evutil_socket_t ev_fd;short ev_events;short ev_res; struct event_base *ev_base;union {struct {LIST_ENTRY (event) ev_io_next;struct timeval ev_timeout;// 当服务于持续性超时event时这里存储超时间隔} ev_io;struct {LIST_ENTRY (event) ev_signal_next;short ev_ncalls;short *ev_pncalls;} ev_signal;} ev_;struct timeval ev_timeout;// 服务于超时event时这里存储下个超时时间点
};我们之前已经从服务于套接字事件分发的角度介绍过eventevent_base及其各个字段含义这里我们立足与服务于定时机制介绍相关字段含义 (1). ev_evcallback服务于定时机制时同样借助ev_evcallback指定事件被分发时的处理信息 (2). ev_timeout_posevent服务于定时机制时需加入到event_base相应结构中 event_base为服务于定时的event准备了两类可选结构通用结构堆 被放置于堆结构时这里存储在堆内索引 被放置于通用结构时由于位于同一通用结构内各个event基于链表连接这里用于构建链接信息 (3). ev_fd event_base对指定event支持其服务于三种类型信号处理套接字事件分发定时机制 其中套接字事件分发与定时机制可同时存在于一个event身上 某个event同时服务于套接字事件分发定时机制时ev_fd用于存储套接字描述符 某个event独立服务于定时机制时ev_fd应设置为-1 (4). ev_events 某个event同时服务于套接字事件分发定时机制时ev_events用于存储感兴趣的套接字事件类型 某个event独立服务于定时机制时ev_events应设置为0 (5). ev_res 若一个服务于定时机制的event因为超时而被分发ev_res用于存储分发原因超时对应EV_TIMEOUT (6). ev_base 存储关联到的event_base对象指针 (7). ev_ 当服务于定时机制下且event属于持久性event此时ev_io.ev_timeout用来存储超时间隔 如何理解持久性event a. 持久性event a.1. 初次添加到event_base后从添加时间点或指定时间点开始直到达到超时间隔期间该event未被分发时会被自动分发一次分发原因将是超时 a.2. 每次event被分发即将执行其回调处理前依据当前时间点结合超时间隔计算下一次超时时间点并再次注册到event_base b. 非持久性event b.1. 初次添加到event_base后从添加时间点或指定时间点开始直到达到超时间隔期间该event未被分发时会被自动分发一次分发原因将是超时若在此期间因为其他原因得到分发分发处理前此event会自动从event_base移除这样后续不会再被event_base分发了
实现上服务于超时的event因为超时被分发时会自动从event_base移除在服务于超时的event即将被回调处理前判断若是持久性的会重新计算下个超时时间点并再次添加到event_base
作为对比服务于套接字的event因为套接字事件被分发时不会从event_base移除但在event即将被回调处理前判断若是非持久性的会将此event从event_base移除 (8). ev_timeout 服务于定时机制时用于存储此event将发生超时的超时时间点信息
3.event_callback对定时机制的支持 我们从支持定时机制角度分析其各个字段
struct event_callback
{TAILQ_ENTRY(event_callback) evcb_active_next;short evcb_flags;ev_uint8_t evcb_pri; /* smaller numbers are higher priority */ev_uint8_t evcb_closure;/* allows us to adopt for different types of events */union {void (*evcb_callback)(evutil_socket_t, short, void *);void (*evcb_selfcb)(struct event_callback *, void *);void (*evcb_evfinalize)(struct event *, void *);void (*evcb_cbfinalize)(struct event_callback *, void *);} evcb_cb_union;void *evcb_arg;
};(1). evcb_active_next 用于将分发的event_callback组织在链表结构 (2). evcb_flags 当此event_callback依附的event被插入服务于定时的堆结构或通用结构后将包含EVLIST_TIMEOUT (3). evcb_pri 特权级 (4). evcb_closure 当此event_callback依附的event服务于定时机制时依据是否是持久的event 对持久的eventevcb_closure为EV_CLOSURE_EVENT相应的evcb_cb_union为evcb_callback 对非持久的eventevcb_closure为EV_CLOSURE_EVENT_PERSIST相应的evcb_cb_union为evcb_callback (5). evcb_cb_union 参考上述 (6). evcb_arg 自定义回调参数
3.event_base对定时支持 (1). 提供通用结构堆结构来容纳服务于定时的event 放置在通用结构的一组event依赖一个内部的放置在堆结构的event实现分发处理 (2). 对放置在通用结构的event 其ev_timeout结构中tv_usec结构符合如下结构 高4比特位为tag固定为0x5 中间8比特位用作索引 低20位为实际数值 对持久的超时event其用于时间间隔的ev_.ev_io.ev_timeout也是如此结构 (3). event_base的事件循环会在每次io复用器分发后依据当前时间点对超时的各个event执行分发处理每次事件循环进入io阻塞等待前也会保证最大等待时间不会超过当前时间点距离最近一个会超时event超时时间点的间隔
4.io复用器对定时支持 io复用器在支持timerfd的情况下允许利用timefd而非epoll_wait的超时参数的方式实现阻塞等待的超时唤醒仅仅较新的linux内核支持此特性
libevent中的信号机制
1.概述 前面我们分析eventevent_callbackevent_base时立足于服务于套接字的event来展开分析的 libevent中的event既可以用于支持对套接字的自动分法手动分发也可用于支持定时机制还可用于支持信号处理
这一部分立足于支持信号机制来分析libevent中eventevent_callbackevent_base
2.服务于信号处理的event
struct event {struct event_callback ev_evcallback;/* for managing timeouts */union {TAILQ_ENTRY(event) ev_next_with_common_timeout;size_t min_heap_idx;} ev_timeout_pos;evutil_socket_t ev_fd;short ev_events;short ev_res; /* result passed to event callback */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_;struct timeval ev_timeout;
};我们从信号处理视角分析各个字段 (1). ev_evcallback 包含回调处理信息 (2). ev_timeout_pos 服务于定时分发时存储所在结构内位置信息 (3). ev_fd 服务于信号处理时这里存储要处理的信号编号 (4). ev_events 服务于信号处理时必然包含EV_SIGNAL类似套接字event信号处理event也支持持久非持久概念对持久的event必然包含EV_PERSIST 对非持久的服务于信号处理的event因为信号产生被分发后即从event_base移除 对持久的服务于信号处理的event信号产生被分发后不会从event_base移除除非显式请求 (5). ev_res 服务于信号处理的event因为信号产生而被分发时这里包含EV_SIGNAL (6). ev_base 指向关联到的event_base (7). ev_ ev_signal中各个字段含义如下 a. ev_signal_next 类似套接字event服务于同一个信号编号的多个event通过链式结构关联在一起ev_signal_next用于构成链式结构 b. ev_ncalls 信号event被分发时允许设置一次分发时回调处理执行多次这里记录回调处理要执行的次数 c. ev_pncalls 这里安排这个指针的意义在于 在信号event的回调处理进行中让ev_pncalls指向ev_ncalls如果再次期间另一个用户将此event从event_base移除 移除时会通过ev_pncalls设置其指向变量为0从而使得当前信号处理回调可以尽快结束
4.event_base事件循环对信号处理的支持
struct event_base {const struct eventop *evsigsel;struct evsig_info sig;struct event_signal_map sigmap;
};我们立足于服务于信号处理的角度截取event_base中相关字段并分析其含义 (1). evsigsel 套接字event的自动分发依赖io复用器信号event的分发也有自己的复用器来提供支持 (2). sig
struct evsig_info {struct event ev_signal;evutil_socket_t ev_signal_pair[2];int ev_signal_added;int ev_n_signals_added;
#ifdef EVENT__HAVE_SIGACTIONstruct sigaction **sh_old;
#elseev_sighandler_t **sh_old;
#endifint sh_old_max;
};这个结构来为event_base实现信号处理提供支持其各个字段含义如下 a. ev_signalev_signal_pair 我们需要一个独立的event来帮助实现信号event的分发 在event_base的初始化阶段会创建一个pipe得到两个套接字并执行如下初始化
event_assign(base-sig.ev_signal, base, base-sig.ev_signal_pair[0], EV_READ | EV_PERSIST, evsig_cb, base);
static void evsig_cb(evutil_socket_t fd, short what, void *arg)
{static char signals[1024];ev_ssize_t n;int i;int ncaught[NSIG];struct event_base *base;base arg;memset(ncaught, 0, sizeof(ncaught));while (1) {
#ifdef _WIN32n recv(fd, signals, sizeof(signals), 0);
#elsen read(fd, signals, sizeof(signals));// 读取pipe套接字数据
#endifif (n -1) {int err evutil_socket_geterror(fd);if (! EVUTIL_ERR_RW_RETRIABLE(err))event_sock_err(1, fd, %s: recv, __func__);// 异常break;// 表示已经读取干净} else if (n 0) {break;// 表示pipe套接字被关闭了}// 对读取内容逐个字节分析for (i 0; i n; i) {ev_uint8_t sig signals[i];if (sig NSIG)ncaught[sig];// 每个字节代表一个信号编号}}EVBASE_ACQUIRE_LOCK(base, th_base_lock);for (i 0; i NSIG; i) {// 允许特定信号的event被分发时分发中执行多次回调处理if (ncaught[i])evmap_signal_active_(base, i, ncaught[i]);// 实现信号event分发}EVBASE_RELEASE_LOCK(base, th_base_lock);
}这样得到的效果时若我们向使得某个信号编号下的各个event得到分发处理我们只需向base-sig.ev_signal_pair[0]套接字写入信号编号即可 b. ev_signal_added 非0时表示依附的event_base上至少注册了一个服务于信号处理的event c. ev_n_signals_added 记录依附的event_base上注册的服务于信号处理的event的数量 d. sh_oldsh_old_max 当我们向event_base注册服务于某个信号编号的event时会通过服务于信号处理的复用器执行信号处理函数安装安装时我们通过sh_old来记录此信号编号安装前的信号处理信息这样当此信号编号的最后一个event从event_base移除时我们需要借助sh_old中的信号处理信息来恢复
5.服务于信号处理的复用器
// 专门服务于信号分发的复用器
static const struct eventop evsigops {signal,NULL,evsig_add,evsig_del,NULL,NULL,0, 0, 0
};当我们最初向event_base添加某个信号编号的event时将执行一次evsig_add当我们将某个信号编号的最后一个event从event_base移除时将执行一次evsig_del a. evsig_add
// 代表开始监控某个信号
static int evsig_add(struct event_base *base, evutil_socket_t evsignal, short old, short events, void *p)
{struct evsig_info *sig base-sig;(void)p;EVUTIL_ASSERT(evsignal 0 evsignal NSIG);EVSIGBASE_LOCK();// 这意味着至少两个event_base各自在监控信号eventif (evsig_base ! base evsig_base_n_signals_added) {event_warnx(Added a signal to event base %p with signals already added to event_base %p. Only one can have signals at a time with the %s backend. The base with the most recently added signal or the most recent event_base_loop() call gets preference; do not rely on this behavior in future Libevent versions.,base, evsig_base, base-evsel-name);}// 通过静态结构记录最近负责信号event监控的event_base// 及其上监控的信号数目evsig_base base;evsig_base_n_signals_added sig-ev_n_signals_added;// 通过pipe的套接字实现分发通知evsig_base_fd base-sig.ev_signal_pair[1];EVSIGBASE_UNLOCK();event_debug((%s: %d: changing signal handler, __func__, (int)evsignal));// 信号处理设置if (evsig_set_handler_(base, (int)evsignal, evsig_handler) -1) {goto err;}if (!sig-ev_signal_added) {if (event_add_nolock_(sig-ev_signal, NULL, 0))goto err;sig-ev_signal_added 1;}return (0);
err:EVSIGBASE_LOCK();--evsig_base_n_signals_added;--sig-ev_n_signals_added;EVSIGBASE_UNLOCK();return (-1);
}上述过程主要为 为信号编号安装信号处理evsig_set_handler_处理中会安装信号处理同时将此信号编号之前的信息处理保存起来 这样后续此信号产生时将执行一次evsig_handler evsig_handler中则通过向evsig_base_fd写入此信号编号内容由于sig-ev_signal的作用这将引发event_base后续对此信号相关的所有event执行一次分发 b. evsig_del
static int evsig_del(struct event_base *base, evutil_socket_t evsignal, short old, short events, void *p)
{EVUTIL_ASSERT(evsignal 0 evsignal NSIG);event_debug((%s: EV_SOCK_FMT: restoring signal handler, __func__, EV_SOCK_ARG(evsignal)));EVSIGBASE_LOCK();--evsig_base_n_signals_added;--base-sig.ev_n_signals_added;EVSIGBASE_UNLOCK();return (evsig_restore_handler_(base, (int)evsignal));
}当某个信号编号的最后一个event从event_base移除时会执行一次evsig_del evsig_del会更新变量并借助evsig_restore_handler_恢复此信号编号之前的信号处理
6.最佳实践 因为linux多线程应用中发给应用的信号只会传递给一个线程激发一次信号处理 所以相应的借助event_base的event实现信号处理时我们应该 (1). 将服务于信号处理的event集中注册到一个event_base libevent中相应的设置了以下局部静态变量来存储此event_base及关联信息
#ifndef EVENT__DISABLE_THREAD_SUPPORT
static void *evsig_base_lock NULL;
#endif
static struct event_base *evsig_base NULL;
static int evsig_base_n_signals_added 0;
static evutil_socket_t evsig_base_fd -1;libevent中通信对象的流量控制
1.流量控制初始化 流控初始化时会设置好时间点最大单次允许量速率间隔单位 2.读写操作前更新并获取允许流量 以读为例每次读时依据上次余量距离上次读取间隔速率最大单次允许量可以计算出本次操作允许量从而控制本次操作时允许读取的量进而达到流控效果 3.读写操作后再次更新允许流量 以读为例每次读后依据读取量上次余量重新计算余量在余量降低到0时候会通过移除可读事件来临时禁止后续读取操作并同时启动一个定时event在定时回调中重新计算余量在余量大于0时重新恢复可读事件并取消定时event
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/913974.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!