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. shell-function 删除目录和文件

    function sDelDirFile() { if [ "$#" -eq 1 ];then if [ -e "$1" ];then rm "$1& ...

  2. Fiddler实战之拟2G、3G、4G网络进行弱网测试

    至于fidder网络代理设置就不多说了 模拟网速: 1.启动Fiddler,打开菜单栏Rules---Performances---Simulate Modem Speeds这里打开了模拟调节速度 2 ...

  3. F - Robot Motion 栈加BFS

    A robot has been programmed to follow the instructions in its path. Instructions for the next direct ...

  4. Volatile的应用DCL单例模式(四)

    Volatile的应用 单例模式DCL代码 首先回顾一下,单线程下的单例模式代码 /** * 单例模式 * * @author xiaocheng * @date 2020/4/22 9:19 */ ...

  5. OkHttp 优雅封装 HttpUtils 之 气海雪山初探

    曾经在代码里放荡不羁,如今在博文中日夜兼行,只为今天与你分享成果.如果觉得本文有用,记得关注我,我将带给你更多. 介绍 HttpUtils 是近期开源的对 OkHttp 轻量封装的框架,它独创的异步预 ...

  6. vue2.x学习笔记(二十二)

    接着前面的内容:https://www.cnblogs.com/yanggb/p/12633051.html. 自定义指令 简介 除了核心功能默认内置的指令([v-mode]和[v-show]等),v ...

  7. [转+自]disable_functions之巧用LD_PRELOAD突破

    写在前面: 通过知乎的一篇艰难的渗透提权,引发了一些对于disable_funcionts绕过的思考,虽然在暑假日记中记载了四种绕过disable_functions,比如com组件,pcntl_ex ...

  8. 开源软件SoftEther使用

    最近在寻找比较好用的开源VPN,感觉SoftEther很符合我的需求.一方面是SoftEther属于开源软件并且一直在更新,另一方面是功能强大,好用. VPN支持路由功能和NAT功能,还支持多种类型的 ...

  9. deepin15.11小毛病解决

    目录 边缘花屏问题 QQ`Tim头像问题 ssh卡死问题 看直播卡 边缘花屏问题 sudo apt install systemsettings 打开kde系统设置 打开显示与设置,修改如图下,基本上 ...

  10. 还学的动吗? 盘点下Vue.js 3.0.0 那些让人激动的功能

    转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 原文出处:https://blog.bitsrc.io/vuejs-3-0-0-beta-features- ...