libevent源码深度剖析八

——集成信号处理
张亮

现在我们已经了解了libevent的基本框架:事件管理框架和事件主循环。上节提到了libevent中I/O事件和Signal以及Timer事件的集成,这一节将分析如何将Signal集成到事件主循环的框架中。

1 集成策略——使用socket pair

前一节已经做了足够多的介绍了,基本方法就是采用“消息机制”。在libevent中这是通过socket pair完成的,下面就来详细分析一下。
      Socket pair就是一个socket对,包含两个socket,一个读socket,一个写socket。工作方式如下图所示:
                               

创建一个socket pair并不是复杂的操作,可以参见下面的流程图,清晰起见,其中忽略了一些错误处理和检查。


              
Libevent提供了辅助函数evutil_socketpair()来创建一个socket pair,可以结合上面的创建流程来分析该函数。

2 集成到事件主循环——通知event_base

Socket pair创建好了,可是libevent的事件主循环还是不知道Signal是否发生了啊,看来我们还差了最后一步,那就是:为socket pair的读socket在libevent的event_base实例上注册一个persist的读事件。
      这样当向写socket写入数据时,读socket就会得到通知,触发读事件,从而event_base就能相应的得到通知了。
前面提到过,Libevent会在事件主循环中检查标记,来确定是否有触发的signal,如果标记被设置就处理这些signal,这段代码在各个具体的I/O机制中,以Epoll为例,在epoll_dispatch()函数中,代码片段如下:

[cpp] view plain copy
  1. res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout);
  2. if (res == -1) {
  3. if (errno != EINTR) {
  4. event_warn("epoll_wait");
  5. return (-1);
  6. }
  7. evsignal_process(base);// 处理signal事件
  8. return (0);
  9. } else if (base->sig.evsignal_caught) {
  10. evsignal_process(base);// 处理signal事件
  11. }

完整的处理框架如下所示:
         
注1:libevent中,初始化阶段并不注册读socket的读事件,而是在注册信号阶段才会测试并注册;
注2:libevent中,检查I/O事件是在各系统I/O机制的dispatch()函数中完成的,该dispatch()函数在event_base_loop()函数中被调用;

3 evsignal_info结构体

Libevent中Signal事件的管理是通过结构体evsignal_info完成的,结构体位于evsignal.h文件中,定义如下:

[cpp] view plain copy
  1. struct evsignal_info {
  2. struct event ev_signal;
  3. int ev_signal_pair[2];
  4. int ev_signal_added;
  5. volatile sig_atomic_t evsignal_caught;
  6. struct event_list evsigevents[NSIG];
  7. sig_atomic_t evsigcaught[NSIG];
  8. #ifdef HAVE_SIGACTION
  9. struct sigaction **sh_old;
  10. #else
  11. ev_sighandler_t **sh_old;
  12. #endif
  13. int sh_old_max;
  14. };

下面详细介绍一下个字段的含义和作用:
1)ev_signal, 为socket pair的读socket向event_base注册读事件时使用的event结构体;
2)ev_signal_pair,socket pair对,作用见第一节的介绍;
3)ev_signal_added,记录ev_signal事件是否已经注册了;
4)evsignal_caught,是否有信号发生的标记;是volatile类型,因为它会在另外的线程中被修改;
5)evsigvents[NSIG],数组,evsigevents[signo]表示注册到信号signo的事件链表;
6)evsigcaught[NSIG],具体记录每个信号触发的次数,evsigcaught[signo]是记录信号signo被触发的次数;
7)sh_old记录了原来的signal处理函数指针,当信号signo注册的event被清空时,需要重新设置其处理函数;
    evsignal_info的初始化包括,创建socket pair,设置ev_signal事件(但并没有注册,而是等到有信号注册时才检查并注册),并将所有标记置零,初始化信号的注册事件链表指针等。

4 注册、注销signal事件

注册signal事件是通过evsignal_add(struct event *ev)函数完成的,libevent对所有的信号注册同一个处理函数evsignal_handler(),该函数将在下一段介绍,注册过程如下:
1 取得ev要注册到的信号signo;
2 如果信号signo未被注册,那么就为signo注册信号处理函数evsignal_handler();
3 如果事件ev_signal还没哟注册,就注册ev_signal事件;
4 将事件ev添加到signo的event链表中;
从signo上注销一个已注册的signal事件就更简单了,直接从其已注册事件的链表中移除即可。如果事件链表已空,那么就恢复旧的处理函数;
下面的讲解都以signal()函数为例,sigaction()函数的处理和signal()相似。
处理函数evsignal_handler()函数做的事情很简单,就是记录信号的发生次数,并通知event_base有信号触发,需要处理:

[cpp] view plain copy
  1. static void evsignal_handler(int sig)
  2. {
  3. int save_errno = errno; // 不覆盖原来的错误代码
  4. if (evsignal_base == NULL) {
  5. event_warn("%s: received signal %d, but have no base configured", __func__, sig);
  6. return;
  7. }
  8. // 记录信号sig的触发次数,并设置event触发标记
  9. evsignal_base->sig.evsigcaught[sig]++;
  10. evsignal_base->sig.evsignal_caught = 1;
  11. #ifndef HAVE_SIGACTION
  12. signal(sig, evsignal_handler); // 重新注册信号
  13. #endif
  14. // 向写socket写一个字节数据,触发event_base的I/O事件,从而通知其有信号触发,需要处理
  15. send(evsignal_base->sig.ev_signal_pair[0], "a", 1, 0);
  16. errno = save_errno; // 错误代码
  17. }

5 小节
本节介绍了libevent对signal事件的具体处理框架,包括事件注册、删除和socket pair通知机制,以及是如何将Signal事件集成到事件主循环之中的。

libevent源码深度剖析八相关推荐

  1. libevent源码深度剖析

    原文地址:http://blog.csdn.net/sparkliang/article/details/4957667 libevent源码深度剖析一 --序幕 张亮 1 前言 Libevent是一 ...

  2. libevent源码深度剖析十一

    libevent源码深度剖析十一 --时间管理 张亮 为了支持定时器,Libevent必须和系统时间打交道,这一部分的内容也比较简单,主要涉及到时间的加减辅助函数.时间缓存.时间校正和定时器堆的时间值 ...

  3. libevent源码深度剖析十

    libevent源码深度剖析十 --支持I/O多路复用技术 张亮 Libevent的核心是事件驱动.同步非阻塞,为了达到这一目标,必须采用系统提供的I/O多路复用技术,而这些在Windows.Linu ...

  4. libevent源码深度剖析一

    libevent源码深度剖析一 --序幕 张亮 1 前言 Libevent是一个轻量级的开源高性能网络库,使用者众多,研究者更甚,相关文章也不少.写这一系列文章的用意在于,一则分享心得:二则对libe ...

  5. libevent 源码深度剖析十三

    libevent 源码深度剖析十三 -- libevent 信号处理注意点 前面讲到了 libevent 实现多线程的方法,然而在多线程的环境中注册信号事件,还是有一些情况需要小心处理,那就是不能在多 ...

  6. libevent源码深度剖析十二

    libevent源码深度剖析十二 --让libevent支持多线程 张亮 Libevent本身不是多线程安全的,在多核的时代,如何能充分利用CPU的能力呢,这一节来说说如何在多线程环境中使用libev ...

  7. libevent源码深度剖析九

    libevent源码深度剖析九 --集成定时器事件 张亮 现在再来详细分析libevent中I/O事件和Timer事件的集成,与Signal相比,Timer事件的集成会直观和简单很多.Libevent ...

  8. libevent源码深度剖析六

    libevent源码深度剖析六 --初见事件处理框架 张亮 前面已经对libevent的事件处理框架和event结构体做了描述,现在是时候剖析libevent对事件的详细处理流程了,本节将分析libe ...

  9. libevent源码深度剖析五

    libevent源码深度剖析五 --libevent的核心:事件event张亮 对事件处理流程有了高层的认识后,本节将详细介绍libevent的核心结构event,以及libevent对event的管 ...

最新文章

  1. 决策树算法实现(train+test,matlab) 转
  2. redis 数据库主从不一致问题解决方案
  3. MVC Filter
  4. 新闻网大数据实时分析可视化系统项目——14、Spark2.X环境准备、编译部署及运行...
  5. 2.12 向量化更多例子-深度学习-Stanford吴恩达教授
  6. python3中numpy函数tile的用法
  7. Java Web 技术栈
  8. 可见光能量范围_JACS:游书力团队通过可见光促进的吲哚衍生物分子内去芳构化合成环丁烷稠合的四环吲哚螺环...
  9. Python3 Ocr 初探
  10. Android Studio两模块间getLaunchIntentForPackage跳转,出现intent为null,已解决
  11. vim 查找匹配字符串次数
  12. 2020.3二级中选择题文件类型题目全套
  13. HDU1236 排名【排序】
  14. CCF NOI1098 森林
  15. 使用Adobe Acrobat为PDF文件添加图章(仅图片)
  16. 无线条码仓库管理系统
  17. maven 阿里源配置完整 亲测有效
  18. 全场景效能平台猪齿鱼 VS Jira
  19. internal, switching, leakage power区别
  20. 二维列表的转置(行列互换,首行变首列,尾行变尾列)

热门文章

  1. VS2010和opencv2.4.9的配置
  2. InnoDB 存储引擎体系架构
  3. mybatis深入理解(一)之 # 与 $ 区别以及 sql 预编译
  4. git rebase教程
  5. 深入理解Java对象序列化
  6. 科研人员的办公室是怎样的?
  7. LaTex:算法排版
  8. Ubuntu 10.10配置JRE、JDK、Eclipse和Tomcat7.0.5
  9. Apache Mahout 简介 通过可伸缩、商业友好的机器学习来构建智能应用程序
  10. Fisher Vector(FV)向量