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. 7.3 java 成员变量和局部变量区别

    /* * 成员变量和局部变量的区别: * A:在类中的位置不同 * 成员变量:类中,方法外 * 局部变量:方法中或者方法声明上(形式参数) * B:在内存中的位置不同 * 成员变量:堆内存 * 局部变 ...

  2. 2、使用断言(json assertion)

    1.假设现在有一个服务端的返回数据(需要测试的)为:HTTP/1.1 200 OK,要测试的响应字段勾选Response Headers,模式匹配规则选择Substring,把该响应断言命名为Http ...

  3. tf.nn.softmax_cross_entropy_with_logits 分类

    tf.nn.softmax_cross_entropy_with_logits(logits, labels, name=None) 参数: logits:就是神经网络最后一层的输出,如果有batch ...

  4. tf.nn.dropout 激活函数

    tf.nn.dropout(x,keep_prob,noise_shape=None,seed=None,name=None) 参数: x:一个浮点型Tensor. keep_prob:一个标量Ten ...

  5. springIoc中的单列对象的分析

    最近有个同事去面试,其中有一个问题是关于spring单例的.本篇博文就发表一下小编我自己的理解~~. 使用过spring的程序猿应该都知道,我们的bean(controller.service和Dao ...

  6. Web三维编程入门总结之一:WebGL与Threejs入门知识

    /*在这里对这段时间学习的3D编程知识做个总结,以备再次出发.计划分成“webgl与three.js基础介绍”.“面向对象的基础3D场景框架编写”.“模型导入与简单3D游戏编写”三个部分,其他零散知识 ...

  7. 提升效率必备!8个超好用的Python内置函数

    文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: 吃着不想停 PS:如有需要Python学习资料的小伙伴可以加点击下方链接自 ...

  8. Jingwen‘s update

    Bugs: The checkin button of the question answering page must be pressed twice to check in the result ...

  9. 今天探究的CSS属性是box-sizing;

    首先BOX-SIZING属性是CSS3的属性: 语法: box-sizing : content-box || border-box || inherit 取值说明 1.content-box:此值为 ...

  10. Flutter Weekly Issue 52

    教程 一个易迁移.兼容性高的 Flutter 富文本方案 复杂业务如何保证Flutter的高性能高流畅度? 插件 flutter_color_models A wrapper for the Dart ...