libevent支持io事件,timeout事件,signal事件,这篇文件将分析libevent是如何组织signal事件,以及如何实现signal事件响应的。

1.  sigmap

类似于io事件,event_base有另外一个hash表sigmap用于存储signal事件,hash表使用signal number做数组索引,同一个signal number的不同事件使用双向链表连接(使用struct event结构中的ev_. ev_signal. ev_signal_next成员来构造这个链表)。使用到的数据结构有struct event_signal_map与struct evmap_signal,定义如下:

/* Used to map signal numbers to a list of events.  If EVMAP_USE_HT is not
defined, this structure is also used as event_io_map, which maps fds to a
list of events.
*/
struct event_signal_map {
/* An array of evmap_io * or of evmap_signal *; empty entries are
* set to NULL. */
void **entries;
/* The number of entries available in entries */
int nentries;
};
/* An entry for an evmap_signal list: notes all the events that want to know
when a signal triggers. */
struct evmap_signal {
struct event_dlist events;
};

event_base中的成员sigmap即是struct event_signal_map结构的实例。entries指向一个动态分配的指针数组,数组长度为nentries;数组成员是一个指向动态分配的evmap_sinal结构的指针。sigmap的结构大致可以用图1-1表示,

图1-1 sigmap存储结构

2.  signal后端

signal事件的后端与io事件的后端不同,它们的操作基本不相同,libevent在源文件signal.c中实现signal事件的后端操作,并在event_base中使用成员struct eventop *evsigsel专门指向signal事件的后端操作,未复用指向io后端的evsel指针,signal后端使用到的信息保存在struct evsig_info sig成员中。

struct evsig_info的定义如下,这里主要介绍ev_signal与ev_signal_pair[2]这两个成员。

/* Data structure for the default signal-handling implementation in signal.c
*/
struct evsig_info {
/* Event watching ev_signal_pair[1] */
struct event ev_signal;
/* Socketpair used to send notifications from the signal handler */
evutil_socket_t ev_signal_pair[];
/* True iff we've added the ev_signal event yet. */
int ev_signal_added;
/* Count of the number of signals we're currently watching. */
int ev_n_signals_added; /* Array of previous signal handler objects before Libevent started
* messing with them. Used to restore old signal handlers. */
#ifdef EVENT__HAVE_SIGACTION
struct sigaction **sh_old;
#else
ev_sighandler_t **sh_old;
#endif
/* Size of sh_old. */
int sh_old_max;
};
  • ev_signal_pair[2]是两个文件描述符,通常使用pipe函数创建。
  • ev_signal是一个专职事件(内部事件),监听ev_signal_pair[0]上的读事件,它作为一个io事件被加入到event_base中,并将优先级设置为0,以便一旦该事件发生,它的回调函数能被优先执行。

在signal后端中,每一个被注册了事件的signal都会使用sigaction函数将signal handler修改为统一的函数static void __cdecl evsig_handler(int sig),这个函数的作用就是向ev_signal_pair[1]中写触发的signal_number,激活事件ev_signal。函数定义如下(删除了跨平台代码):

static void __cdecl
evsig_handler(int sig)
{
int save_errno = errno;
ev_uint8_t msg; if (evsig_base == NULL) {
event_warnx(
"%s: received signal %d, but have no base configured",
__func__, sig);
return;
} /* Wake up our notification mechanism */
msg = sig;
{
int r = write(evsig_base_fd, (char*)&msg, );
(void)r; /* Suppress 'unused return value' and 'unused var' */
} errno = save_errno;
}

ev_signal的回调函数也是在signal.c源文件中定义,static void evsig_cb(evutil_socket_t fd, short what, void *arg),它的作用就是从ev_signal_pair[0]上读回被触发的signal number,并将sigmap上对应signal_number的所有event加入到event_base的待执行回调函数中。函数定义如下:

/* Callback for when the signal handler write a byte to our signaling socket */
static void
evsig_cb(evutil_socket_t fd, short what, void *arg)
{
static char signals[];
ev_ssize_t n;
int i;
int ncaught[NSIG];
struct event_base *base; base = arg;
memset(&ncaught, , sizeof(ncaught)); while () { n = read(fd, signals, sizeof(signals)); if (n == -) {
int err = evutil_socket_geterror(fd);
if (! EVUTIL_ERR_RW_RETRIABLE(err))
event_sock_err(, fd, "%s: recv", __func__);
break;
} else if (n == ) {
/* XXX warn? */
break;
}
for (i = ; i < n; ++i) {
ev_uint8_t sig = signals[i];
if (sig < NSIG)
ncaught[sig]++;
}
} EVBASE_ACQUIRE_LOCK(base, th_base_lock);
for (i = ; i < NSIG; ++i) {
if (ncaught[i])
evmap_signal_active_(base, i, ncaught[i]);//将signal number i 对应的事件加入到待执行回调函数链表中
}
EVBASE_RELEASE_LOCK(base, th_base_lock);
}

当signal发生时,evsig_handler会向ev_signal_pair[1]写数据(写入singal number),激活ev_signal事件,ev_signal事件的回调函数evsig_cb会从ev_signal_pair[0]上读到被触发的signal number,然后据此从event_base的sigmap中将对应的所有event的回调函数加入到待执行回调函数链表,这些回调函数将在event_base_loop中被执行,完成对signal事件的响应。

libevent源码分析三--signal事件响应的更多相关文章

  1. libevent源码分析二--timeout事件响应

    libevent不仅支持io事件,同时还支持timeout事件与signal事件,这篇文件将分析libevent是如何组织timeout事件以及如何响应timeout事件. 1.  min_heap ...

  2. libevent源码分析一--io事件响应

    这篇文章将分析libevent如何组织io事件,如何捕捉事件的发生并进行相应的响应.这里不会详细分析event与event_base的细节,仅描述io事件如何存储与如何响应. 1.  select l ...

  3. 【转】libevent源码分析

    libevent源码分析 转自:http://www.cnblogs.com/hustcat/archive/2010/08/31/1814022.html 这两天没事,看了一下Memcached和l ...

  4. Libevent源码分析 (1) hello-world

    Libevent源码分析 (1) hello-world ⑨月份接触了久闻大名的libevent,当时想读读源码,可是由于事情比较多一直没有时间,现在手头的东西基本告一段落了,我准备读读libeven ...

  5. 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入

    使用react全家桶制作博客后台管理系统   前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...

  6. [Abp vNext 源码分析] - 13. 本地事件总线与分布式事件总线 (Rabbit MQ)

    一.简要介绍 ABP vNext 封装了两种事件总线结构,第一种是 ABP vNext 自己实现的本地事件总线,这种事件总线无法跨项目发布和订阅.第二种则是分布式事件总线,ABP vNext 自己封装 ...

  7. tomcat源码分析(三)一次http请求的旅行-从Socket说起

    p { margin-bottom: 0.25cm; line-height: 120% } tomcat源码分析(三)一次http请求的旅行 在http请求旅行之前,我们先来准备下我们所需要的工具. ...

  8. libevent源码分析

    这两天没事,看了一下Memcached和libevent的源码,做个小总结. 1.入门 1.1.概述Libevent是一个用于开发可扩展性网络服务器的基于事件驱动(event-driven)模型的网络 ...

  9. Django搭建及源码分析(三)---+uWSGI+nginx

    每个框架或者应用都是为了解决某些问题才出现旦生的,没有一个事物是可以解决所有问题的.如果觉得某个框架或者应用使用很不方便,那么很有可能就是你没有将其使用到正确的地方,没有按开发者的设计初衷来使用它,当 ...

随机推荐

  1. 将zabbix服务和monitor服务在一个机器上部署

    问题,两个服务的文件路径都是 /usr/local/sdata下,要让两个服务共存,至少需要讲一个服务的文件迁移到别的文件夹,同时将所有的配置项都进行修改,使能找到指定的文件路径, 方案1,先按照za ...

  2. [PHP] Laravel 5.5 打印SQL语句

    [PHP] Laravel 5.5 打印SQL语句 四种方法 第一种方法: 打印SQL默认是关闭的,需要在/vendor/illuminate/database/Connection.php中打开. ...

  3. BZOJ2151/洛谷P1792 题解

    若想要深入学习反悔贪心,传送门. Description: 有 \(n\) 个位置,每个位置有一个价值.有 \(m\) 个树苗,将这些树苗种在这些位置上,相邻位置不能都种.求可以得到的最大值或无解信息 ...

  4. SpringMVC相关试题

    1.下列相关Spring自动装配的说法中,错误的是( ). (选择一项) A:在Spring配置文件中,可以通过<bean>元素的autowire属性指定自动装配方式B: autowire ...

  5. [技术博客]使用adb命令获取app(游戏)错误和警告日志

    adb命令的使用 直接在命令行中输入: adb logcat *:W 注意:这句命令的意思是显示所有优先级大于等于警告(Warning)的日志,查找崩溃问题一般用: adb logcat *:E 注意 ...

  6. Netty通信网络参数配置

    Netty服务端/客户端网络通信过程中常用的参数: Name Associated setter method "writeBufferHighWaterMark" 默认64 * ...

  7. Java编程思想之五初始化与清理

    随着计算机革命的发展,"不安全"的编程方式已经逐渐称为编程代价高昂的主因之一. 初始化和清理正是涉及安全的两个问题. 5.1 用构造器确保初始化 通过提供构造器,类的设计者可确保每 ...

  8. poi导出word表格跨行

    DataCommon.java package com.ksource.pwlp.model.statistic; public class DataCommon { private Long id; ...

  9. 【深入学习linux】git的使用

    git的安装 官网下载地址:https://git-scm.com/downloads 安装完成后,还需要最后一步设置,在命令行输入: $ git config --global user.name ...

  10. FNV哈希算法(转)

    由来:FNV哈希算法全名为Fowler-Noll-Vo算法,是以三位发明人Glenn Fowler,Landon Curt Noll,Phong Vo的名字来命名的,最早在1991年提出. 特点和用途 ...