libevent通过socketpair实现对信号事件的监听。

还记得event_base吗?

struct event_base {
struct evsig_info sig;
// 略
};

evsig_info结构如下:

struct evsig_info {
evutil_socket_t ev_signal_pair[]; /* Socketpair used to send notifications from the signal handler */
struct event ev_signal; /* Event watching ev_signal_pair[1] */ int ev_signal_added; /* True if we've added the ev_signal event yet. */
int ev_n_signals_added; /* Count of the number of signals we're currently watching. */ struct sigaction **sh_old; /* sigaction* 指针数组,用于存放信号处理函数 */
int sh_old_max; /* Size of sh_old. */
};

evsig_init

在event_base初始化阶段会完成socketpair的创建。

int
evsig_init(struct event_base *base)
{
/*
* Our signal handler is going to write to one end of the socket
* pair to wake up our event loop. The event loop then scans for
* signals that got delivered.
*/
if (evutil_socketpair(
AF_UNIX, SOCK_STREAM, , base->sig.ev_signal_pair) == -) {
#ifdef WIN32
/* Make this nonfatal on win32, where sometimes people
have localhost firewalled. */
event_sock_warn(-, "%s: socketpair", __func__);
#else
event_sock_err(, -, "%s: socketpair", __func__);
#endif
return -;
} evutil_make_socket_closeonexec(base->sig.ev_signal_pair[]);
evutil_make_socket_closeonexec(base->sig.ev_signal_pair[]);
base->sig.sh_old = NULL;
base->sig.sh_old_max = ; evutil_make_socket_nonblocking(base->sig.ev_signal_pair[]);
evutil_make_socket_nonblocking(base->sig.ev_signal_pair[]); event_assign(&base->sig.ev_signal, base, base->sig.ev_signal_pair[],
EV_READ | EV_PERSIST, evsig_cb, base); base->sig.ev_signal.ev_flags |= EVLIST_INTERNAL;
event_priority_set(&base->sig.ev_signal, ); base->evsigsel = &evsigops; return ;
}

可以看到,sig.ev_signal关联了sig.ev_signal_pair[1]的读事件,并且回调函数为evsig_cb

evsig_add

假设我们现在向event_base添加一个信号事件signal_int

struct event signal_int;
event_assign(&signal_int, base, SIGINT, EV_SIGNAL|EV_PERSIST, signal_cb, &signal_int);
event_add(&signal_int, NULL);

event_add最终会调用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 >= && evsignal < NSIG); /* catch signals if they happen quickly */
EVSIGBASE_LOCK();
if (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);
}
evsig_base = base;
evsig_base_n_signals_added = ++sig->ev_n_signals_added;
evsig_base_fd = base->sig.ev_signal_pair[0];
EVSIGBASE_UNLOCK(); event_debug(("%s: %d: changing signal handler", __func__, (int)evsignal));
if (_evsig_set_handler(base, (int)evsignal, evsig_handler) == -) {
goto err;
} if (!sig->ev_signal_added) {
if (event_add(&sig->ev_signal, NULL))
goto err;
sig->ev_signal_added = ;
} return (); err:
EVSIGBASE_LOCK();
--evsig_base_n_signals_added;
--sig->ev_n_signals_added;
EVSIGBASE_UNLOCK();
return (-);
}

evsig_add做了两件事:

1. 设置信号的回调函数为evsig_handler

  回调函数存放于sig.sh_old中。

2. 将I/O事件sig.ev_signal添加到epoll中。

整篇文章的两个主角出现了: evsig_handler、evsig_cb。

evsig_handler

static void __cdecl
evsig_handler(int sig)
{
int save_errno = errno;
#ifdef WIN32
int socket_errno = EVUTIL_SOCKET_ERROR();
#endif
ev_uint8_t msg; if (evsig_base == NULL) {
event_warnx(
"%s: received signal %d, but have no base configured",
__func__, sig);
return;
} #ifndef _EVENT_HAVE_SIGACTION
signal(sig, evsig_handler);
#endif /* Wake up our notification mechanism */
msg = sig;
send(evsig_base_fd, (char*)&msg, , );
errno = save_errno;
#ifdef WIN32
EVUTIL_SET_SOCKET_ERROR(socket_errno);
#endif
}

这里evsig_base_fd = base->sig.ev_signal_pair[0];

evsig_cb

/* 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 = recv(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]);
}
EVBASE_RELEASE_LOCK(base, th_base_lock);
}

这里的recv置于while中,说明每次evsig_cb运行时,会将sig.ev_signal_pair[0]上的数据全部读完。

总结

信号事件流程

signal_int、sig.ev_signal

每个事件都有个事件类型。

signal_int的事件类型为EV_SIGNAL,说明它是一个信号事件。

经过上述的流程之后,signal_int会被加入到信号事件队列sigmap,以及总事件队列eventqueue。

sig.ev_signal的事件类型为EV_READ,说明它是一个I/O事件。

经过上述的流程之后,sig.ev_signal会被加入到I/O事件队列io,以及总事件队列eventqueue。

同时,与sig.ev_signal关联的fd会被加入epoll中。

libevent(七)信号事件监听的更多相关文章

  1. libevent(六)事件监听

    libevent是如何实现事件监听的呢? 在Linux,libevent的底层实现是epoll,因此实现事件监听的方式就是,把需要监听的fd加入epoll中. I/O事件 定时器事件 定时器事件没有f ...

  2. 前端基本知识(四):JS的异步模式:1、回调函数;2、事件监听;3、观察者模式;4、promise对象

    JavaScript语言将任务的执行模式可以分成两种:同步(Synchronous)和异步(Asychronous). “同步模式”就是一个任务完成之后,后边跟着一个任务接着执行:程序的执行顺序和排列 ...

  3. java Gui编程 事件监听机制

    1.     GUI编程引言 以前的学习当中,我们都使用的是命令交互方式: 例如:在DOS命令行中通过javac java命令启动程序. 软件的交互的方式:   1. 命令交互方式    图书管理系统 ...

  4. Spring Boot学习笔记:ApplicationEvent和ApplicationEventListener事件监听

    采用事件监听的好处 以用户注册的业务逻辑为例,用户在填写完信息表单后,提交信息到后台,后台对用户信息进行处理,然后给用户返回处理结果的信息. 如上图所示,用户在注册时,后台需要处理一些系列流程,实际业 ...

  5. WebView使用详解(二)——WebViewClient与常用事件监听

      登录|注册     关闭 启舰 当乌龟有了梦想……       目录视图 摘要视图 订阅 异步赠书:Kotlin领衔10本好书      免费直播:AI时代,机器学习如何入门?      程序员8 ...

  6. Java中用得比较顺手的事件监听

    第一次听说监听是三年前,做一个webGIS的项目,当时对Listener的印象就是个"监视器",监视着界面的一举一动,一有动静就触发对应的响应. 一.概述 通过对界面的某一或某些操 ...

  7. 4.JAVA之GUI编程事件监听机制

    事件监听机制的特点: 1.事件源 2.事件 3.监听器 4.事件处理 事件源:就是awt包或者swing包中的那些图形用户界面组件.(如:按钮) 事件:每一个事件源都有自己特点有的对应事件和共性事件. ...

  8. Node.js 教程 05 - EventEmitter(事件监听/发射器 )

    目录: 前言 Node.js事件驱动介绍 Node.js事件 注册并发射自定义Node.js事件 EventEmitter介绍 EventEmitter常用的API error事件 继承EventEm ...

  9. .NET事件监听机制的局限与扩展

    .NET中把“事件”看作一个基本的编程概念,并提供了非常优美的语法支持,对比如下C#和Java代码可以看出两种语言设计思想之间的差异. // C#someButton.Click += OnSomeB ...

随机推荐

  1. mysql导出

    --all-databases , -A 导出全部数据库. mysqldump -uroot -p --all-databases --all-tablespaces , -Y 导出全部表空间. my ...

  2. Python设计模式(4)-装饰模式

    # coding=utf-8 # * 一般情况下,当一个基类写好之后,我们也许不愿意去改动,也不能改动,原因是# * 这样的在项目中用得比较久的基类,一旦改动,也许会影响其他功能模块,但是,# * 又 ...

  3. MyBatis通用 Mapper4使用小结

    官网地址: http://www.mybatis.tk/ https://gitee.com/free 1.使用springboot,添加依赖: 使用tk的mybatis后不需要引用官方原生的myba ...

  4. AJ学IOS(13)UI之UITableView学习(下)汽车名牌带右侧索引

    AJ分享,必须精品 先看效果图 代码 ViewController #import "NYViewController.h" #import "NYCarGroup.h& ...

  5. Thinking in Java,Fourth Edition(Java 编程思想,第四版)学习笔记(三)之Everything Is an Object

    ---恢复内容开始--- Both C++ and Java are hybird languages. A hybird language allow multiple programming st ...

  6. ajax按楼层加载数据

    代码如下: <!doctype html> <html> <head> <meta charset="utf-8"> <tit ...

  7. Present CodeForces - 1323D (思维+二分)

    题目大意比较简单,就是求一堆(二元组)的异或和. 思路:按位考虑,如果说第k位为1的话,那么一定有奇数个(二元组)在该位为1.二元组内的数是相加的,相加是可以进位的.所以第k位是0还是1,至于k为后边 ...

  8. D - Free Candies UVA - 10118

    题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem ...

  9. A - Smith Numbers POJ

    While skimming his phone directory in 1982, Albert Wilansky, a mathematician of Lehigh University,no ...

  10. leetcode-0101 对称二叉树

    题目地址 https://leetcode-cn.com/problems/symmetric-tree/ 1.递归 本题最简单的思路是递归,可以假设两棵一模一样的树在进行镜像对比.他们之间的关系满足 ...