新看看官方demo的libevent如何使用信号
  1. int called = 0;
  2. static void
  3. signal_cb(int fd, short event, void *arg)
  4. {
  5. struct event *signal = arg;
  6. printf("%s: got signal %d\n", __func__, EVENT_SIGNAL(signal));
  7. if (called >= 2)
  8. event_del(signal);

  9. called++;
  10. }
  11. int
  12. main (int argc, char **argv)
  13. {
  14. struct event signal_int;//这里我们把它称为事件2
  15. /* Initalize the event library */
  16. event_init();
  17. /* 初始化事件2,设置相关信号,回调函数 */
  18. event_set(&signal_int, SIGINT, EV_SIGNAL|EV_PERSIST, signal_cb,
  19. &signal_int);
  20. event_add(&signal_int, NULL);//激活信号
  21. event_dispatch();//等待事件的触发
  22. return (0);
  23. }
这里只提信号相关操作~~~
event_init()有关信号操作将跳转到epoll.c中的epoll_init->evsignal_init
  1. evsignal_init(base);

接下来分析evsignal_init函数,event_base结构体中有信号管理结构evsignal_info(注意不是指针)
而evsignal_info结构本身有一个event事件,这里称为事件1(这里很关键)
  1. struct evsignal_info {
  2. struct event ev_signal;//向event_base注册读事件使用的event结构体,这里我们称为事件1
  3. int ev_signal_pair[2];//sock pair对,也就是clientfd跟servfd
  4. int ev_signal_added;//记录ev_signal信号是否已经注册
  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. };
evsignal_init具体流程:event_init中最终会使用evsignal_init(base),来看看做了什么事
创建了一对Socketpair(作用:信号来临时,通过信号处理函数socketpair写端发送字节,事件1监听socketpair的读fd,触发事件1),并且将读的fd与事件1相关联,并且将事件1与event_base结构体的指针相关联   
  1. int
  2. evsignal_init(struct event_base *base)
  3. {
  4. int i;
  5. /*
  6. * Our signal handler is going to write to one end of the socket
  7. * pair to wake up our event loop. The event loop then scans for
  8. * signals that got delivered.
  9. *///创建一个socketpair
  10. if (evutil_socketpair(
  11. AF_UNIX, SOCK_STREAM, 0, base->sig.ev_signal_pair) == -1) {
  12. #ifdef WIN32
  13. /* Make this nonfatal on win32, where sometimes people
  14. have localhost firewalled. */
  15. event_warn("%s: socketpair", __func__);
  16. #else
  17. event_err(1, "%s: socketpair", __func__);
  18. #endif
  19. return -1;
  20. }
  21. ////子进程不能访问该socketpair
  22. FD_CLOSEONEXEC(base->sig.ev_signal_pair[0]);
  23. FD_CLOSEONEXEC(base->sig.ev_signal_pair[1]);
  24. base->sig.sh_old = NULL;
  25. base->sig.sh_old_max = 0;
  26. base->sig.evsignal_caught = 0;
  27. memset(&base->sig.evsigcaught, 0, sizeof(sig_atomic_t)*NSIG);
  28. /* initialize the queues for all events */
  29. for (i = 0; i < NSIG; ++i)
  30. TAILQ_INIT(&base->sig.evsigevents[i]);
  31. //设置为非阻塞
  32. evutil_make_socket_nonblocking(base->sig.ev_signal_pair[0]);
  33. //可读事件设置与fd相关(但还缺乏注册到base注册链表中,需要在event_add中才会被注册到链表中去)
  34. event_set(&base->sig.ev_signal, base->sig.ev_signal_pair[1],
  35. EV_READ | EV_PERSIST, evsignal_cb, &base->sig.ev_signal);
  36. base->sig.ev_signal.ev_base = base;//把信号对应的事件跟base相关联
  37. base->sig.ev_signal.ev_flags |= EVLIST_INTERNAL;
  38. return 0;
  39. }

接着分析event_add函数,实际对应epoll_add函数(以epoll举例)
实际调用的是evsignal_add函数:这里唯一注意的是(事件2并不是注册到base而是挂载到信号链表,因为信号对应的fd为-1,并没有什么卵用)

  1. //添加事件
  2. static int
  3. epoll_add(void *arg, struct event *ev)
  4. {
  5. struct epollop *epollop = arg;//获取epoll管理结构体
  6. struct epoll_event epev = {0, {0}};//epoll事件
  7. struct evepoll *evep;//读写事件指针
  8. int fd, op, events;
  9. if (ev->ev_events & EV_SIGNAL)//是否注册了信号
  10. return (evsignal_add(ev));//是的话,添加信号到此事件
  11. fd = ev->ev_fd;//获取对应的描述符
  12. if (fd >= epollop->nfds) {//判断描述符是否大于最大值,是的话,扩充
  13. /* Extent the file descriptor array as necessary */
  14. if (epoll_recalc(ev->ev_base, epollop, fd) == -1)
  15. return (-1);
  16. }
  17. evep = &epollop->fds[fd];//获取事件指针
  18. op = EPOLL_CTL_ADD;//默认是添加,其实还有修改等
  19. events = 0;
  20. if (evep->evread != NULL) {//这里epoll的修改与添加设置到一起了,如果不为空,说明本身已有事件了,那就只是修改器读写而已
  21. events |= EPOLLIN;//修改
  22. op = EPOLL_CTL_MOD;//设置为默认
  23. }
  24. if (evep->evwrite != NULL) {//为空
  25. events |= EPOLLOUT;
  26. op = EPOLL_CTL_MOD;
  27. }
  28. if (ev->ev_events & EV_READ)//是否可读
  29. events |= EPOLLIN;//events设置为可读
  30. if (ev->ev_events & EV_WRITE)//是否可写
  31. events |= EPOLLOUT;//设置为可写,注意events只是int类型
  32. epev.data.fd = fd;//epoll事件设置fd
  33. epev.events = events;//epoll事件设置为是否可读可写
  34. if (epoll_ctl(epollop->epfd, op, ev->ev_fd, &epev) == -1)
  35. return (-1);
  36. /* Update events responsible */
  37. if (ev->ev_events & EV_READ)//更新ev_events是否可读可写,如果是,那就更新evep读写事件指针,表示此事件可读可写
  38. evep->evread = ev;
  39. if (ev->ev_events & EV_WRITE)
  40. evep->evwrite = ev;
  41. return (0);
  42. }
evsignal_add函数分析
设置信号处理函数evsignal_handler,将事件1注册到base中(这样就可以响应了),并且将ev_signal_added标志设置为1,表示因事件1注册而表示有信号加入。
同时将事件2挂载到信号链表(通过信号值为索引)的末端。
  1. int
  2. evsignal_add(struct event *ev)
  3. {
  4. int evsignal;
  5. struct event_base *base = ev->ev_base;
  6. struct evsignal_info *sig = &ev->ev_base->sig;//获取信号事件结构体
  7. if (ev->ev_events & (EV_READ|EV_WRITE))//信号事件不可以是读写
  8. event_errx(1, "%s: EV_SIGNAL incompatible use", __func__);
  9. evsignal = EVENT_SIGNAL(ev);//信号的fd就是信号的number
  10. assert(evsignal >= 0 && evsignal < NSIG); // //信号不能超过NSIG这个数
  11. if (TAILQ_EMPTY(&sig->evsigevents[evsignal])) {////如果说该信号链表为空
  12. event_debug(("%s: %p: changing signal handler", __func__, ev));
  13. if (_evsignal_set_handler( //设置信号处理函数,同时,保存原来的信号处理函数到ev_base->sh_old中去
  14. base, evsignal, evsignal_handler) == -1)
  15. return (-1);
  16. /* catch signals if they happen quickly */
  17. evsignal_base = base;
  18. if (!sig->ev_signal_added) {//判断是否已加入
  19. if (event_add(&sig->ev_signal, NULL))//正式注册,添加到epoll_wait中去
  20. return (-1);
  21. sig->ev_signal_added = 1;//表示已经添加了
  22. }
  23. }
  24. //把ev->ev_signal_next加入到sig->evsigevents[evsignal]的链表末端
  25. /* multiple events may listen to the same signal */
  26. TAILQ_INSERT_TAIL(&sig->evsigevents[evsignal], ev, ev_signal_next);
  27. return (0);
  28. }

event_dispatch()分析,实际调用epoll_dispatch,而epoll_dispatch实际调用evsignal_process。

  1. static int
  2. epoll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
  3. {
  4. struct epollop *epollop = arg; //获取管理epoll的结构
  5. struct epoll_event *events = epollop->events;//epoll的事件数组
  6. struct evepoll *evep;
  7. int i, res, timeout = -1;
  8. if (tv != NULL)
  9. timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;//设置超时事件
  10. if (timeout > MAX_EPOLL_TIMEOUT_MSEC) {//不可以大于最大超时事件
  11. /* Linux kernels can wait forever if the timeout is too big;
  12. * see comment on MAX_EPOLL_TIMEOUT_MSEC. */
  13. timeout = MAX_EPOLL_TIMEOUT_MSEC;
  14. }
  15. res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout);
  16. if (res == -1) {
  17. if (errno != EINTR) {
  18. event_warn("epoll_wait");
  19. return (-1);
  20. }
  21. evsignal_process(base);//处理信号事件
  22. return (0);
  23. } else if (base->sig.evsignal_caught) {
  24. evsignal_process(base);//处理信号事件
  25. }
  26. event_debug(("%s: epoll_wait reports %d", __func__, res));
  27. for (i = 0; i < res; i++) {
  28. int what = events[i].events;
  29. struct event *evread = NULL, *evwrite = NULL;
  30. int fd = events[i].data.fd;
  31. if (fd < 0 || fd >= epollop->nfds)
  32. continue;
  33. evep = &epollop->fds[fd];
  34. if (what & (EPOLLHUP|EPOLLERR)) {
  35. evread = evep->evread;
  36. evwrite = evep->evwrite;
  37. } else {
  38. if (what & EPOLLIN) {//可读
  39. evread = evep->evread;
  40. }
  41. if (what & EPOLLOUT) {//可写
  42. evwrite = evep->evwrite;
  43. }
  44. }
  45. if (!(evread||evwrite))
  46. continue;
  47. if (evread != NULL)//插入就绪链表
  48. event_active(evread, EV_READ, 1);
  49. if (evwrite != NULL)//插入就绪链表
  50. event_active(evwrite, EV_WRITE, 1);
  51. }
  52. if (res == epollop->nevents && epollop->nevents < MAX_NEVENTS) {
  53. /* We used all of the event space this time. We should
  54. be ready for more events next time. */
  55. int new_nevents = epollop->nevents * 2;
  56. struct epoll_event *new_events;
  57. new_events = realloc(epollop->events,
  58. new_nevents * sizeof(struct epoll_event));
  59. if (new_events) {
  60. epollop->events = new_events;
  61. epollop->nevents = new_nevents;
  62. }
  63. }
  64. return (0);
  65. }
evsignal_process
假设有信号发生了~将调用信号处理函数设置信号发生标志位同时发送一个字节数据到socketpair的读端,
而事件1恰好是监听此读端,所以epoll_wait返回然后看信号触发位是否设置为1了,设置了将调用evsignal_process()
而evsignal_process函数内容是遍历信号链表看是否有挂载的事件,有的话,将该事件2插入已就绪链表中,另外也将事件1插入就绪链表
  1. void
  2. evsignal_process(struct event_base *base)//遍历信号链表,是否有事件2挂载
  3. {
  4. struct evsignal_info *sig = &base->sig;//获取信号管理结构体
  5. struct event *ev, *next_ev;
  6. sig_atomic_t ncalls;
  7. int i;
  8. base->sig.evsignal_caught = 0;
  9. for (i = 1; i < NSIG; ++i) {
  10. ncalls = sig->evsigcaught[i];//是否有触发
  11. if (ncalls == 0)//没有就可以滚了
  12. continue;
  13. sig->evsigcaught[i] -= ncalls;//有的话。。。清空
  14. for (ev = TAILQ_FIRST(&sig->evsigevents[i]);
  15. ev != NULL; ev = next_ev) {
  16. next_ev = TAILQ_NEXT(ev, ev_signal_next);
  17. if (!(ev->ev_events & EV_PERSIST))//没设置这个位,就只使用一次了
  18. event_del(ev);
  19. event_active(ev, EV_SIGNAL, ncalls);//插入就绪链表
  20. }
  21. }
  22. }

总流程:
event_base结构体中有信号管理结构evsignal_info(注意不是指针)
而evsignal_info结构本身有一个event事件,这里称为事件1(这里很关键)

event_init中最终会使用evsignal_init(base),来看看做了什么事
创建了一对Socketpair(作用:通过信号处理函数发送字节,事件1监听读fd,触发事件1发生),并且将读的fd与事件1相关联,并且将事件1与event_base结构体的指针相关联


然后我们在main函数中,创建一个事件2,通过初始化信号2
event_set(&signal_int, SIGINT, EV_SIGNAL|EV_PERSIST, signal_cb,
   &signal_int);

然后event_add实际使用的是epoll_add然后把事件插入到已注册链表
看看epoll_add实际是evsignal_add
设置信号处理函数evsignal_handler,将事件1注册到base中(这样就可以响应了),并且将ev_signal_added设置为1,表示因事件1注册而表示有信号加入。
同时将事件2挂载到信号链表(通过信号值查找)的末端(貌似事件2并不是注册到base而是挂载到链表,因为信号对应的fd为-1,并没有什么卵用)

evsignal_handler信号处理函数的内容如下
设置信号已经触发位为1,触发次数+1
同时发送1个字节数据到socketpair的读端



当当当~接下来开始dispatch了

~~~假设有信号发生了~将调用信号处理函数设置信号发生标志位同时发送一个字节数据到socketpair的读端,
而事件1恰好是监听此读端,所以epoll_wait返回然后看信号触发位是否设置为1了,设置了将调用evsignal_process()
而evsignal_process函数内容是遍历信号链表看是否有挂载的事件,有的话,将该事件2插入已就绪链表中,另外也将事件1插入就绪链表

接着发现有就绪事件,就调用event_process_active(),实际也就是调用其事件对应的回调函数完成处理~~End

libevent源码分析之信号处理的更多相关文章

  1. 【转】libevent源码分析

    libevent源码分析 转自:http://www.cnblogs.com/hustcat/archive/2010/08/31/1814022.html 这两天没事,看了一下Memcached和l ...

  2. Libevent源码分析 (1) hello-world

    Libevent源码分析 (1) hello-world ⑨月份接触了久闻大名的libevent,当时想读读源码,可是由于事情比较多一直没有时间,现在手头的东西基本告一段落了,我准备读读libeven ...

  3. libevent源码分析

    这两天没事,看了一下Memcached和libevent的源码,做个小总结. 1.入门 1.1.概述Libevent是一个用于开发可扩展性网络服务器的基于事件驱动(event-driven)模型的网络 ...

  4. Libevent源码分析系列【转】

    转自:https://www.cnblogs.com/zxiner/p/6919021.html 1.使用libevent库     源码那么多,该怎么分析从哪分析呢?一个好的方法就是先用起来,会用了 ...

  5. Libevent源码分析系列

    1.使用libevent库     源码那么多,该怎么分析从哪分析呢?一个好的方法就是先用起来,会用了,然后去看底层相应的源码,这样比较有条理,自上向下掌握.下面用libevent库写个程序,每隔1秒 ...

  6. Libevent源码分析—event_base_dispatch()

    我们知道libevent是一个Reactor模式的事件驱动的网络库.   到目前为止,我们已经看了核心的event和event_base结构体的源码,看了初始化这两个结构体的源码,看了注册event的 ...

  7. libevent源码分析二--timeout事件响应

    libevent不仅支持io事件,同时还支持timeout事件与signal事件,这篇文件将分析libevent是如何组织timeout事件以及如何响应timeout事件. 1.  min_heap ...

  8. libevent源码分析一--io事件响应

    这篇文章将分析libevent如何组织io事件,如何捕捉事件的发生并进行相应的响应.这里不会详细分析event与event_base的细节,仅描述io事件如何存储与如何响应. 1.  select l ...

  9. Libevent源码分析—event_init()

    下面开始看初始化event_base结构的相关函数.相关源码位于event.c event_init() 首先调用event_init()初始化event_base结构体 struct event_b ...

随机推荐

  1. POJ-3126 BFS,埃式筛选及黑科技

    题目大意:给定两个四位素数a  b,要求把a变换到b,变换的过程要保证  每次变换出来的数都是一个 四位素数,而且当前这步的变换所得的素数  与  前一步得到的素数  只能有一个位不同,而且每步得到的 ...

  2. 容器技术Docker

    什么是decker容器 简单理解就是将代码和部署环境一起打包的一个容器

  3. erlang节点互通查看

    在局域网内部,一般用短节点名来完成短节点的全联通.     全联通的前提之一是cookie要相同,cookie记录在一个文件中.     对于同一个物理机上的两个erlang节点,不用其他配置就可以全 ...

  4. 《Cracking the Coding Interview》——第10章:可扩展性和存储空间限制——题目7

    2014-04-24 22:06 题目:搜索引擎问题,如果有一列100台服务器的集群,用来响应查询请求,你要如何设计query分发和cache策略? 解法:query分发可以用计算数字签名并对机器数取 ...

  5. 《Cracking the Coding Interview》——第4章:树和图——题目3

    2014-03-19 03:34 题目:给定一个排好序的数组,设计算法将其转换为一棵二叉搜索树,要求树的高度最小. 解法:递归生成平衡二叉树,使左右子树的节点数尽量相等,所以对半开最好了.其实也可以生 ...

  6. springboot示例参考网站

    https://blog.csdn.net/u013248535/article/details/55100979/

  7. ASP NET Core ---Automapper

    官方文档:http://docs.automapper.org/en/stable/index.html 一.安装和配置: 二.使用: 1.建立 Profile文件: public class Map ...

  8. Python全栈工程师(元组、字典)

     ParisGabriel     感谢 大家的支持  你们的阅读评价就是我最好的更新动力  我会坚持吧排版做的越来越好        每天坚持 一天一篇 点个订阅吧  灰常感谢    当个死粉也阔以 ...

  9. 洛谷P2678跳石头(提高)

    题目背景 一年一度的“跳石头”比赛又要开始了! 题目描述 这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石.组委会已经选择好了两块岩石作为比赛起点和终点. 在起点和终点之间,有 N 块岩石( ...

  10. PAT——乙级1028

    这道题花了我半个多小时,对呀乙级算是挺多时间的了. 1028 人口普查 (20 point(s)) 某城镇进行人口普查,得到了全体居民的生日.现请你写个程序,找出镇上最年长和最年轻的人. 这里确保每个 ...