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事件的响应。

最新文章

  1. 常用CSS样式
  2. iOS学习笔记---oc语言第六天
  3. 【POJ】1505 Copying Books
  4. 学习《Python核心编程》做一下知识点提要,方便复习(一)
  5. 这才是正确删除 office 的方式
  6. Oracle SQL篇(三)Oracle ROWNUM 与TOP N分析
  7. java8 泛型声明 The diamond operator (&quot;&lt;&gt;&quot;) should be used
  8. 关于ubuntu服务器上部署postgresql 以及安装pgadmin4管理工具(web版)
  9. vue小项目---管理系统
  10. 完美支持Py3的微信开发库推荐
  11. 基于CAS在.NET中实现SSO单点登录
  12. python day05
  13. 字符串与NUll的拼接问题
  14. 添加本地jar包到maven仓库
  15. php正则讲解 及与 js的正则比较
  16. 【leetcode】20.有效的括号
  17. InstallShield2015制作安装包----------安装后实现电脑开机自启动
  18. Webwork【05】请求跳转前 xwork.xml 的读取
  19. Trove系列(四)—Trove的快照功能介绍
  20. 开源且功能强大的C# 扩展方法类库Pure.Ext,包含1000+个拓展方法 (支持.Net Framework和.Net Core)

热门文章

  1. CENTOS 7 下安装 REDIS 5.0.6 完整步骤
  2. # [SDOI2019]移动金币 阶梯博弈 dp
  3. cogs 920. [東方S1] 琪露诺
  4. linux 系统添加jdk环境变量
  5. (转) 中断处理程序&amp;中断服务例程
  6. quartz 1.6.2之前的版本,定时任务自动停掉问题
  7. 敏捷项目管理—Scrum框架总结
  8. Fiddler添加过滤条件
  9. com.alibaba.druid.pool.DruidPooledConnection cannot be cast to oracle.jdbc.OracleConnection 异常解决办法
  10. 小米手机root