libevent(七)信号事件监听
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(七)信号事件监听的更多相关文章
- libevent(六)事件监听
libevent是如何实现事件监听的呢? 在Linux,libevent的底层实现是epoll,因此实现事件监听的方式就是,把需要监听的fd加入epoll中. I/O事件 定时器事件 定时器事件没有f ...
- 前端基本知识(四):JS的异步模式:1、回调函数;2、事件监听;3、观察者模式;4、promise对象
JavaScript语言将任务的执行模式可以分成两种:同步(Synchronous)和异步(Asychronous). “同步模式”就是一个任务完成之后,后边跟着一个任务接着执行:程序的执行顺序和排列 ...
- java Gui编程 事件监听机制
1. GUI编程引言 以前的学习当中,我们都使用的是命令交互方式: 例如:在DOS命令行中通过javac java命令启动程序. 软件的交互的方式: 1. 命令交互方式 图书管理系统 ...
- Spring Boot学习笔记:ApplicationEvent和ApplicationEventListener事件监听
采用事件监听的好处 以用户注册的业务逻辑为例,用户在填写完信息表单后,提交信息到后台,后台对用户信息进行处理,然后给用户返回处理结果的信息. 如上图所示,用户在注册时,后台需要处理一些系列流程,实际业 ...
- WebView使用详解(二)——WebViewClient与常用事件监听
登录|注册 关闭 启舰 当乌龟有了梦想…… 目录视图 摘要视图 订阅 异步赠书:Kotlin领衔10本好书 免费直播:AI时代,机器学习如何入门? 程序员8 ...
- Java中用得比较顺手的事件监听
第一次听说监听是三年前,做一个webGIS的项目,当时对Listener的印象就是个"监视器",监视着界面的一举一动,一有动静就触发对应的响应. 一.概述 通过对界面的某一或某些操 ...
- 4.JAVA之GUI编程事件监听机制
事件监听机制的特点: 1.事件源 2.事件 3.监听器 4.事件处理 事件源:就是awt包或者swing包中的那些图形用户界面组件.(如:按钮) 事件:每一个事件源都有自己特点有的对应事件和共性事件. ...
- Node.js 教程 05 - EventEmitter(事件监听/发射器 )
目录: 前言 Node.js事件驱动介绍 Node.js事件 注册并发射自定义Node.js事件 EventEmitter介绍 EventEmitter常用的API error事件 继承EventEm ...
- .NET事件监听机制的局限与扩展
.NET中把“事件”看作一个基本的编程概念,并提供了非常优美的语法支持,对比如下C#和Java代码可以看出两种语言设计思想之间的差异. // C#someButton.Click += OnSomeB ...
随机推荐
- C++语言实现顺序表
C++语言实现顺序表 顺序表的定义及其特点 顺序表的定义是:把线性表中的所有表项按照其逻辑顺序依次存储到从计算机存储中指定存储位置开始的一块连续的存储空间中. 这样,线性表中第一个表项的存储位置就是被 ...
- React Native简史
诞生 React Native 诞生于 2013 年的 Facebook 内部黑客马拉松(hackathon): In the essence of Facebook’s hacker culture ...
- Python设计模式(10)-模板模式
class DbManager: def insert(self): pass def dele(self): pass class DbManager: def insert(self): pass ...
- [一起面试AI]NO.9 如何判断函数凸或非凸
首先定义凸集,如果x,y属于某个集合M,并且所有的θx+(1-θ)f(y)也属于M,那么M为一个凸集.如果函数f的定义域是凸集,并且满足 f(θx+(1-θ)y)≤θf(x)+(1-θ)f(y) 则该 ...
- HTTP 405 的错误提示:消息 JSP 只允许 GET、POST 或 HEAD。Jasper 还允许 OPTIONS 的解决方法
如果项目是运行在 Tomcat 8 及以上,会发现发出的 PUT 请求和 DELETE 请求可以被控制其接收到,但是返回页面时(forward)会报HTTP 405 的错误提示:"消息 JS ...
- 最后の冲刺 for Alpha release 12/15/2015
好开心啊,又吃成长快乐了~ 据说release前一天,团队的工作效率会达到顶峰.亲证无效!!! release当天才是团队智力效力能力的巅峰好不好?!因为,今天,plan B:福昕pdf reader ...
- xshell下使用vim的编辑一个文件Ctrl+S和Ctrl+Q
xshell下使用vim的编辑一个文件,保存的时候习惯性的按了Ctrl+S 结构悲剧了.屏幕锁死了.按其他键都没有反应,exc也不行. 经过问度娘才知道. 原来Ctrl+S在Linux里,是锁定屏幕的 ...
- 用Java代码来校验QQ号
校验qq号码: 1.要求必须是5-15位数字 2.0不能开头 分析: A:键盘录入一个qq号码 B:写一个功能实现校验 C:调用功能,输出结果. 代码实现:public class RegexDemo ...
- Java 使用正则表达式和IO实现爬虫以及503解决
我这边找了个小说网站: 基本套路: 第一步:获取小说每一章的url地址 第二步:获取章节url内容并使用正则表达式提取需要的内容 第三步:多线程封装,实现如下效果 最后测试. 代码: 内容获取封装: ...
- [Abp vNext 入坑分享] - 前言
一·背景 Abp vnext是 ABP 框架作者所发起一个完全基于 ASP .NET Core框架,截至2020年4月份已经升级到2.5.0版本,根据经验2.0版本以后可以放心的使用在生产环境.类似a ...