libevent中将信号集成到event_base_loop事件循环中,通过socketpair转换成I/O事件,本文主要介绍相关的转换。

1、将信号转成I/O

采用socket pair方式,一个socket对,一个用于写,一个用于读,具体工作方式如下

linux环境下直接使用socketpair就可以,而在不支持socketpair情况就需要自己实现socket对,流程图为

libevent中是通过evutil_socketpair来实现的。

2、集成到event_base_loop事件循环中

创建socketpair后,将读socket注册到event_base实例中,当信号发生时,会将signo写入写socket,此时会触发读事件,然后调用signo对应的事件加入到激活事件链表中。

其处理框架为

3、信号初始化

其初始化代码为

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.*///创建socket对if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, base->sig.ev_signal_pair) == -1) {
#ifdef WIN32/* Make this nonfatal on win32, where sometimes peoplehave localhost firewalled. */event_sock_warn(-1, "%s: socketpair", __func__);
#elseevent_sock_err(1, -1, "%s: socketpair", __func__);
#endifreturn -1;}//设置socket对的FD_CLOEXEC标志evutil_make_socket_closeonexec(base->sig.ev_signal_pair[0]);evutil_make_socket_closeonexec(base->sig.ev_signal_pair[1]);base->sig.sh_old = NULL;base->sig.sh_old_max = 0;//设置非阻塞evutil_make_socket_nonblocking(base->sig.ev_signal_pair[0]);evutil_make_socket_nonblocking(base->sig.ev_signal_pair[1]);//设置读socket的回调event_assign(&base->sig.ev_signal, base, base->sig.ev_signal_pair[1],EV_READ | EV_PERSIST, evsig_cb, base);base->sig.ev_signal.ev_flags |= EVLIST_INTERNAL;event_priority_set(&base->sig.ev_signal, 0);base->evsigsel = &evsigops;return 0;
}

4、注册

注册信号事件是通过event_add,在设置信号事件时,会将事件的ev_events包含EV_SIGNAL,所以在event_add_internal函数中会调用evmap_signal_add将事件添加到signo对应的事件链表中,同时会调用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 >= 0 && 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) == -1) {  //设置信号处理函数goto err;}if (!sig->ev_signal_added) {  //读socket是否注册到event_base中,如果没有,就注册到event_base中if (event_add(&sig->ev_signal, NULL))goto err;sig->ev_signal_added = 1;}return (0);err:EVSIGBASE_LOCK();--evsig_base_n_signals_added;--sig->ev_n_signals_added;EVSIGBASE_UNLOCK();return (-1);
}

在介绍之前,先说明evsig_info结构体

struct evsig_info {/* Event watching ev_signal_pair[1] */struct event ev_signal;  //socketpair的读socket事件/* Socketpair used to send notifications from the signal handler */evutil_socket_t ev_signal_pair[2];//socketpair/* True iff we've added the ev_signal event yet. */int ev_signal_added; //读socket事件添加标志/* 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_SIGACTIONstruct sigaction **sh_old; //先前的信息处理函数
#elseev_sighandler_t **sh_old;
#endif/* Size of sh_old. */int sh_old_max;//先前的最大信号值
};

信号处理函数是通过_evsig_set_handler来实现的

int
_evsig_set_handler(struct event_base *base,int evsignal, void (__cdecl *handler)(int))
{
#ifdef _EVENT_HAVE_SIGACTIONstruct sigaction sa;
#elseev_sighandler_t sh;
#endifstruct evsig_info *sig = &base->sig;void *p;/** resize saved signal handler array up to the highest signal number.* a dynamic array is used to keep footprint on the low side.*/if (evsignal >= sig->sh_old_max) { //添加的信号值大于先前的最大信号值,就重新分配先前的信号处理函数指针数组int new_max = evsignal + 1;event_debug(("%s: evsignal (%d) >= sh_old_max (%d), resizing",__func__, evsignal, sig->sh_old_max));p = mm_realloc(sig->sh_old, new_max * sizeof(*sig->sh_old));if (p == NULL) {event_warn("realloc");return (-1);}memset((char *)p + sig->sh_old_max * sizeof(*sig->sh_old),0, (new_max - sig->sh_old_max) * sizeof(*sig->sh_old));sig->sh_old_max = new_max;sig->sh_old = p;}/* allocate space for previous handler out of dynamic array */sig->sh_old[evsignal] = mm_malloc(sizeof *sig->sh_old[evsignal]);if (sig->sh_old[evsignal] == NULL) {event_warn("malloc");return (-1);}/* save previous handler and setup new handler *///注册新的信号处理函数,同时将先前的信号处理函数保存在sh_old数组中
#ifdef _EVENT_HAVE_SIGACTIONmemset(&sa, 0, sizeof(sa));sa.sa_handler = handler;sa.sa_flags |= SA_RESTART;sigfillset(&sa.sa_mask);if (sigaction(evsignal, &sa, sig->sh_old[evsignal]) == -1) {event_warn("sigaction");mm_free(sig->sh_old[evsignal]);sig->sh_old[evsignal] = NULL;return (-1);}
#elseif ((sh = signal(evsignal, handler)) == SIG_ERR) {  event_warn("signal");mm_free(sig->sh_old[evsignal]);sig->sh_old[evsignal] = NULL;return (-1);}*sig->sh_old[evsignal] = sh;
#endifreturn (0);
}

所有的信号处理函数都是evsig_handler,将信号值写入写socket中,触发event_base中事件循环的读事件

static void __cdecl
evsig_handler(int sig)
{int save_errno = errno;
#ifdef WIN32int socket_errno = EVUTIL_SOCKET_ERROR();
#endifev_uint8_t msg;if (evsig_base == NULL) {event_warnx("%s: received signal %d, but have no base configured",__func__, sig);return;}#ifndef _EVENT_HAVE_SIGACTIONsignal(sig, evsig_handler);
#endif/* Wake up our notification mechanism */msg = sig;send(evsig_base_fd, (char*)&msg, 1, 0);  //发送信号值errno = save_errno;
#ifdef WIN32EVUTIL_SET_SOCKET_ERROR(socket_errno);
#endif
}

5、事件触发

读事件触发后,调用evsig_cb,接收信号值,统计信号触发次数,读取信号对应的回调,添加到激活链表中

static void
evsig_cb(evutil_socket_t fd, short what, void *arg)
{static char signals[1024];ev_ssize_t n;int i;int ncaught[NSIG];struct event_base *base;base = arg;memset(&ncaught, 0, sizeof(ncaught));while (1) {n = recv(fd, signals, sizeof(signals), 0);  //接收信号值if (n == -1) {int err = evutil_socket_geterror(fd);if (! EVUTIL_ERR_RW_RETRIABLE(err))event_sock_err(1, fd, "%s: recv", __func__);break;} else if (n == 0) {/* XXX warn? */break;}//统计信号触发次数for (i = 0; i < n; ++i) {ev_uint8_t sig = signals[i];if (sig < NSIG)ncaught[sig]++;}}EVBASE_ACQUIRE_LOCK(base, th_base_lock);//将事件添加到激活链表中for (i = 0; i < NSIG; ++i) {if (ncaught[i])evmap_signal_active(base, i, ncaught[i]);}EVBASE_RELEASE_LOCK(base, th_base_lock);
}

libevent中的信号处理相关推荐

  1. morlet包络检波matlab,布里渊光纤传感系统中的信号处理的研究

    布里渊光纤传感系统中的信号处理的研究2012年5月 TP247崔琳 毕卫红2012年5月 光学工程 TheResearchingonSignalProcessingMethodsofBrillouin ...

  2. libevent中的bufferevent

    bufferevent是libevent中处理网络事件很重要也是比较复杂的一个模块,其中包含一个读事件,一个写事件,两个缓冲区(读和写).读写水位.三个回调(读.写.出错)和函数指针组成的操作集.目前 ...

  3. libevent中的缓冲区(一)

     libevent中的缓冲区定义为evbuffer,主要在文件evbuffer-internal.h文件中,定义如下 struct evbuffer {/** The first chain in ...

  4. libevent中的基本数据结构

     libevent中文件queue.h文件包含5种数据结构:单链表,双向链表,队列,尾队列,环形队列.在处理I/O和signal中的事件时,用的就是尾队列,下面就介绍这几种数据结构 1.单链表 链表 ...

  5. libevent中事件的添加与删除

     前面介绍了libevent中的hash表,在添加事件时,具体是如何操作的呢?事件操作主要是在evmap.c文件中,包含了io事件,signal事件的操作.在事件操作时,分两种情况,一种是利用ha ...

  6. libevent中的时间及相关的管理

    libevent中的时间及相关的管理 在介绍时间之前,先说明几个与时间相关的函数及其用法 1.基础 1.1 clock_gettime(精度比较高,ns级) #include <time.h&g ...

  7. libevent中的hash表

    libevent中的hash表的代码在ht-internal文件中,在添加io事件,signal事件时,底层是在操作  event_io_map和event_signal_map 1. hash的 ...

  8. 在libevent中使用线程池

    一 线程的初始化 1线程对象 在进行事件驱动时,每个线程需建立自己的事件根基.由于libevent未提供线程之间通信的方式,我们采用管道来进行线程的通信.同时为方便主线程分配线程,我们还需保留各个线程 ...

  9. 如何在Python中加速信号处理

    如何在Python中加速信号处理 This post is the eighth installment of the series of articles on the RAPIDS ecosyst ...

最新文章

  1. SAP WM MIGO移动类型311转库过账后WM层面产生了Posting Change Notice?
  2. 神策数据荣登 2020 IDC 中国 Fintech 50 强榜单
  3. 《深入理解C++11:C++ 11新特性解析与应用》——导读
  4. 除了工商银行,目前中国还有那些宇宙级别的企业?
  5. 51nod-猴猴吃香蕉【dp】
  6. pat1111-1120
  7. MySQL百万级/千万级数据存储解决方案
  8. C#LeetCode刷题-双指针
  9. wps序号打乱重新排序_WPS中Excel怎么自动排序
  10. 有重复组合公式及其证明方法
  11. html的3d旋转木马插件,js 3D旋转木马特效插件
  12. 硬件工程师成长之路(2)——电路设计
  13. 实时渲染3D动画创作大赛
  14. Chrome 添加自定义搜索引擎
  15. SuperMap三维专题之3dsMax数据——对接篇
  16. 【高并发编程】再谈同步、异步、阻塞、非阻塞
  17. 嵌入式设备的发展—应对复杂的开发设计挑战
  18. 华为擎云G540笔记本怎么U盘重装电脑系统详细教学
  19. Linux命令(13)——实时监控进程、监控网络
  20. 考研政治与专业课总结(持续更新)

热门文章

  1. 《玩转.NET Micro Framework 移植-基于STM32F10x处理器》--微软中国.NET Micro Framework项目组工程师所作之序...
  2. 利用LSM实现更安全的linux
  3. linux扩充消息队列,Linux关于消息队列的使用分享
  4. python打开一个不存在的文件时-python判断文件是否存在,不存在就创建一个的实例...
  5. python是一种通用编程语言-想自学一种编程语言,各种编程语言都有什么区别?...
  6. python中turtle画酷炫图案-酷炫的动态可视化交互大屏,用Excel就能做!
  7. python 自动化-Python 接口自动化测试
  8. python电脑下载有问题-Python 解决火狐浏览器不弹出下载框直接下载的问题
  9. python简单代码画图-Python竟能画这么漂亮的花,帅呆了(代码分享)
  10. python安装numpy-Python使用pip安装Numpy模块