libevent在对文件描述符,套接字进行监控时直接放到event,这些event通过io多路复用函数进行监控,然而对应信号来说io复用函数却无能为力,为了解决问题,libevent采用统一事件源的方式,即将信号也表现成event的形式,用到了socketpair套接字对

socketpair套接字对
套接字对也是通信方式的一种,在进程间通信时相比于管道和命名管道而言更简单,也更安全
linux下使用socketpair函数创建一对套接字,函数原型

/**@param d: 协议族,通常为AF_UNIX*@param type: 套接字类型,如SOCK_STREAM*@param protocol: 协议,0即可*@param sv: 存放创建的两个套接字*/
int socketpair(int d, int type, int protocol, int sv[2]);

创建的套接字对的每一端都可以进行读写,也就是sv[0]可以既作为读端也可以作为写端,sv[1]也是。
libevent中sv[1]为读端,sv[0]为写端。向sv[0]中写入数据时sv[1]会变为可读


对于信号的处理,其实用户可以自己使用sigaction注册信号处理函数然后自己提供回调函数,但是既然想要交给libevent处理,就说明用户只提供回调函数,其他什么都不管。
也可以在内部只调用sigaciton/signal注册信号处理函数,回调函数是用户提供的那个,这样也可以满足需求,但是既然统一事件源,就需要把信号也当成event处理。

libevent的做法是为

  • 读端注册到base中,提供一个内部的回调函数,该步骤仅仅调用一次
  • 信号值作为fd创建event,绑定内部回调函数,注册到base中
  • 为信号绑定一个内部的信号处理函数,在信号处理函数中将发生的信号值写入套接字对的写端
  • 此时读端变为可读,调用这个读端套接字绑定的回调函数
  • 读端回调函数中读取信号值,将信号值对应的event添加到激活队列中
  • 激活队列处理

我觉得完全可以直接在内部使用sigaction/signal绑定信号值和用户的信号处理函数
但是既然libevent想要把所有东西都当成event来处理,就需要将信号转成event,把所有event都放到激活队列中一起处理


复习一下linux下的信号处理机制

UNIX系统信号机制最简单的接口是signal函数,函数原型为

#include <signal.h>
void (*signal(int signo, void (*func)(int)))(int);
  • signo表示用户关心的信号类型,如终端信号,退出信号,SIGINT,SIGQUIT等
  • func可以是一个函数指针,也就是信号处理函数,当signno信号发生时,会调用这个函数。该函数有一个int类型参数,通常是信号值。也可以是常量SIG_IGN表示内核忽略该信号(SIGKILL和SIGSTOP不能被忽略),常量SIG_DFL表示执行系统默认的处理动作,通常是终止当前进程
  • 返回值是指向在此之前的信号处理函数的指针

signal的使用还是比较简单的

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <sys/types.h>void handler(int signo);int main()
{signal(SIGINT,handler);for(;;)pause();return 0;
}void handler(int signo)
{printf("receive sig=%d\n", sig);
}

程序首先注册信号处理函数,之后当中断发生后切换到内核态,在内核的中断处理程序执行完后返回用户态之前检测到有信号SIGINT发生,内核遍不恢复到main函数而是直接转到handler函数中,在执行完handler后由内核转到main函数中继续执行。main和handler是两个独立的存在


signal并不属于POSIX标准,在UNIX中使用sigaction函数作为signal的替代品,signaction函数的功能是检查或修改(或检查并修改)与制定信号相关联的处理动作,函数原型为

#include <signal.h>
int sigaction(int signo, const struct sigaction *act,struct sigaction *oact);
  • signno是要检测或修改其处理动作的信号编号,如SIGINT,SIGQUIT等
  • act为要修改的动作,本质上是信号处理函数
  • oact为以前的信号处理动作,不关心时可以设置为NULL
  • 调用成功返回0,失败返回-1(linux的系统调用返回值大多是这样)

结构体struct sigaction如下

struct sigaction
{void (*sa_handler)(int);sigset_t sa_mask;int sa_flags;void (*sa_sigaction)(int, siginfo_t *, void *);
}
  • sa_handler和signal的第二个参数相同,表示信号处理函数,SIG_IGN表示忽略,SIG_DFL表示默认动作,参数为信号值
  • sa_mask是信号屏蔽字集,如果设置,那么在进行信号处理函数时,如果有在sa_mask中的信号到达,则不会通知进程,而是在信号处理函数执行完后才通知。默认情况下,如果程序正在执行信号处理函数,那么对于当前这个信号处理函数处理的信号不会再次送到进程。
  • 内核维护着一个信号队列,如果当前有正在处理的信号处理函数,会把其他到达进程的信号放到这个队列中。若同一种信号多次发生,通常只会添加一次
  • sa_sigaction和sa_flags配合使用,与sa_handler相对应,通常使用sa_handler就不需要使用sa_sigaction,主要用于实时信号可以带信息,使用sa_handler时sa_flags设为0即可

通常使用sa_handler和sa_mask就可以满足需求了

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>void handler(int signo);int main()
{struct sigaction act;act.sa_handler = handler;/* 清空信号屏蔽集 */sigemptyset(&act.sa_mask);/* 在信号处理函数执行期间屏蔽SIGQUIT,执行完后送达当前进程 */sigaddset(&act.sa_mask, SIGQUIT);act.sa_flags = 0;if(sigaction(SIGINT, &act, NULL) < 0){perror("sigaction error");exit(1);}for(;;)pause();return 0;
}void handler(int signo)
{printf("receive sig=%d\n", signo);sleep(5);
}

先按ctrl+c发送中断信号,马上按ctrl+z发送终止信号,发现程序5秒后才终止,说明sa_mask中的信号如果发生了会在当前信号处理函数完成后才送达当前进程

libevent源码学习-----统一事件源及信号绑定函数相关推荐

  1. libevent源码学习-----阅读心得

    框架设计思路 libevent使用统一事件源将所有问题都转化为event,比如将套接字/信号/描述符都在内部转化为event,由相应的io多路复用函数进行监控. 为了提供对超时event的支持,lib ...

  2. libevent源码学习-----事件驱动流程分析

    libevent中事件驱动的大体流程如下 /* 创建事件驱动 */ struct event_base* base = event_base_new(); /**创建一个事件*@param base: ...

  3. libevent源码学习-----时间管理

    libevent监听的event有以下几种 文件描述符/套接字,没有设定超时时长 信号 文件描述符/套接字,设定超时时长 对于时间,libevent内部的时间管理是通过最小堆实现的,原因如下 既然某些 ...

  4. libevent源码学习-----event操作

    libevent核心结构是event_base和event,接下来主要介绍event结构 /* event的定义的主要部分 */ struct event {/* ... *//* event监听的描 ...

  5. libevent源码学习----io多路复用的封装和使用

    因为是非阻塞监听事件的发生,所以内部其实还是采用io多路复用函数实现的. 又因为可供选择的io函数很多,linux下有epoll, poll, select等,window下有ICOP, select ...

  6. libevent源码学习-----event_base事件循环

    event_base是libevent的事件驱动,也是Reactor模式的直接体现.任何使用libevent的代码最开始都需要创建一个base,之后的任何接口函数都和这个base关联着,下面是stru ...

  7. libevent源码学习-----Reactor模型

    libevent内部采用了reactor模型 所谓reactor模型,其实就是一套事件注册机制,用来解决单线程的阻塞问题.reactor核心思想是将事件和相应事件发生时想要调用的函数都记录下来,在事件 ...

  8. Libevent 源码学习笔记(1)event 与 event_base

    目录 event event_base eventop evcb_closure event_callback event_changelist evsig_info event_io_map eve ...

  9. Libevent源码学习笔记一:event2/event.h

    一.libevent标准使用方法: 每个程序使用Libevent必须include <event2/event.h> 头文件,并 传给 -levent  链接器.如果只是想使用主要的eve ...

最新文章

  1. linux关机 hibernate,Linux关机命令
  2. ibatis调用sqlserver存储过程
  3. Linux---输入与输出函数总结
  4. Hollis原创|深入分析Java的编译原理
  5. intelx79服务器芯片组,Intel X79芯片组下月开始供货
  6. 《TCP/IP详解卷1:协议》读书笔记
  7. 中班音乐活动 机器人_【家门口的好幼儿园】玩转音乐,释放天性——岳阳幼儿园开展松江区骨干共同体名师二团实践研究活动...
  8. android实现应用商店开发,基于Android平台的应用商店客户端的设计与实现
  9. protel99se进阶视频教程(手把手教你画51单片机开发板)
  10. word、excle、ppt文档图标显示异常的解决方案!
  11. 行人重识别论文阅读14-Learning by Aligning- Visible-Infrared Person Re-identification
  12. ckeditor5 用法
  13. [ C语言版 ] 数独计算器 [ 搜索剪枝法 ]
  14. VBA 获取最大行数和最大列数
  15. php正则校验特殊字符,PHP正则表达式中的特殊字符
  16. 10种人脉裂变技能,6个裂变核心,8个吸粉人性本能怎样玩粉丝裂变?
  17. spack file hierarchy system
  18. 可以ping通外网,ping不通内网,同时可以Ping网关,但是外部ping不到IP
  19. java验证11位手机号_JAVA怎样用数组实现输入手机号码为11位
  20. c语言奇数阶幻方构造法,奇数阶面幻方的行列式构造法.pdf

热门文章

  1. python日志_python日志处理
  2. 【C/C++】知识点系统复习 (第一周)
  3. tiny4412的烧录工具minitool安装【学习笔记】
  4. 杂项-Java:JSP
  5. 关于ESXI能虚拟出多少个虚拟机和CPU的关系
  6. 随机猜拳判断胜利(思路,逻辑正确不完美)if switch
  7. 关于@@IDENTITY、SCOPE_IDENTITY ()、IDENT_CURRENT ('tableName')
  8. hdu_1233(最小生成树)
  9. 继承练习 :开发一个系统时 需要对员工进行建模 员工包含3个属性 姓名 工号 工资 功能 work
  10. .net授权获取openid_Asp.Net Core 中IdentityServer4 授权中心之自定义授权