zoukankan      html  css  js  c++  java
  • libevent1.4 阅读记录三

    前面我们对I/O模型有了一些了解,现在再来说signal。signal事件的出现对于进程来说是随机的,libevent当信号事件发生时,不是用信号的回调函数直接处理信号,而是在回调函数中设法通知I/O模型有信号事件发生让I/O返回,将信号与I/O事件、计时器事件一起处理。

    首先,看一下event_base中的(evsignal_info)sig的结构体

    //evsignal.h
    struct
    evsignal_info { struct event ev_signal;    //这不是信号事件,这是socket pair的读监听事件,所有关注的信号间接关联到这个事件。 int ev_signal_pair[2];    //socket pair对,一对通过本地环回连接的socket,ev_signal_pair[0]为收到信号后回调函数发送I/O的socket,ev_signal_pair[1]为接收I/O的socket。 int ev_signal_added;      //一个标记,表明这个event被注册过 volatile sig_atomic_t evsignal_caught;  //一个标记,如果收到至少一个信号,置为1。sig_atomic_t意思是对这个变量的初始化、赋值都是原子的。 struct event_list evsigevents[NSIG];  //注册到signal的事件链表的数组,数组下标为对应的signal sig_atomic_t evsigcaught[NSIG];  //记录signal事件发生次数的数组,数组下标为对应的signal #ifdef HAVE_SIGACTION struct sigaction **sh_old;  //记录被修改的signal回调函数及信号屏蔽状态。这是一个signal回调函数指针数组,下标对应的signal的值(例如:信号SIGINT的回调函数指针为sh_old[2])。
                        我们注册信号时信号回调函数被改为了向socket pair的一端发送数据,当我们注销某个信号时,需要把该信号的回调函数改回去。
    #else ev_sighandler_t **sh_old; #endif int sh_old_max;  //sh_old数组长度 };

    接下里,从源码看一下signal流程,还是以epoll为例

    1.在epoll_init中调用evsignal_init完成socekt pair的初始化并连接,并将其中的读socket注册成epoll监听事件

     1 int
     2 evsignal_init(struct event_base *base)
     3 {
     4     int i;
     5 
     6     /* 
     7      * Our signal handler is going to write to one end of the socket
     8      * pair to wake up our event loop.  The event loop then scans for
     9      * signals that got delivered.
    10      */                                        //这段注释说的很清楚了,就是signal回调函数发送消息给这个socket pair的一端来唤醒event loop,然后event loop检查signal并处理
    11     if (evutil_socketpair(
    12             AF_UNIX, SOCK_STREAM, 0, base->sig.ev_signal_pair) == -1) {  //在evutil.c中初始化socket pair对,并连接,发送消息的socket存在base->sig.ev_signal_pair[0],
                                                  接收消息的socket存在base->sig.ev_signal_pair[1]
    13 #ifdef WIN32 14 /* Make this nonfatal on win32, where sometimes people 15 have localhost firewalled. */ 16 event_warn("%s: socketpair", __func__); 17 #else 18 event_err(1, "%s: socketpair", __func__); 19 #endif 20 return -1; 21 } 22 23 FD_CLOSEONEXEC(base->sig.ev_signal_pair[0]);    //从定义来看是说在子进程中不能使用这个句柄,也就是子进程不能用这个socket 24 FD_CLOSEONEXEC(base->sig.ev_signal_pair[1]);    //从定义来看是说在子进程中不能使用这个句柄,也就是子进程不能用这个socket 25 base->sig.sh_old = NULL; 26 base->sig.sh_old_max = 0; 27 base->sig.evsignal_caught = 0; 28 memset(&base->sig.evsigcaught, 0, sizeof(sig_atomic_t)*NSIG); 29 /* initialize the queues for all events */ 30 for (i = 0; i < NSIG; ++i) 31 TAILQ_INIT(&base->sig.evsigevents[i]); 32 33 evutil_make_socket_nonblocking(base->sig.ev_signal_pair[0]); 34 evutil_make_socket_nonblocking(base->sig.ev_signal_pair[1]); 35 36 event_set(&base->sig.ev_signal, base->sig.ev_signal_pair[1], 37 EV_READ | EV_PERSIST, evsignal_cb, &base->sig.ev_signal);  //初始化socket pair持久化的读事件,用于接收signal回调发送I/O的消息。注意这里还没有注册,只是初始化了这个事件。 38 base->sig.ev_signal.ev_base = base; 39 base->sig.ev_signal.ev_flags |= EVLIST_INTERNAL; 40 41 return 0; 42 }

    2.接下来在epoll_add中调用了evsignal_add注册了前面socket pair初始化后的读事件,在epoll_add完成后,还会将这个事件添加到已注册链表中

      1 /* Helper: set the signal handler for evsignal to handler in base, so that
      2  * we can restore the original handler when we clear the current one. */
      3 int
      4 _evsignal_set_handler(struct event_base *base,
      5               int evsignal, void (*handler)(int))
      6 {
      7 #ifdef HAVE_SIGACTION
      8     struct sigaction sa;
      9 #else
     10     ev_sighandler_t sh;
     11 #endif
     12     struct evsignal_info *sig = &base->sig;
     13     void *p;
     14 
     15     /*
     16      * resize saved signal handler array up to the highest signal number.
     17      * a dynamic array is used to keep footprint on the low side.
     18      */
     19     if (evsignal >= sig->sh_old_max) {  //扩充保存原来信号回调函数数组的内存,这里还没有为数组元素分配内存
     20         int new_max = evsignal + 1;
     21         event_debug(("%s: evsignal (%d) >= sh_old_max (%d), resizing",
     22                 __func__, evsignal, sig->sh_old_max));
     23         p = realloc(sig->sh_old, new_max * sizeof(*sig->sh_old));
     24         if (p == NULL) {
     25             event_warn("realloc");
     26             return (-1);
     27         }
     28 
     29         memset((char *)p + sig->sh_old_max * sizeof(*sig->sh_old),
     30             0, (new_max - sig->sh_old_max) * sizeof(*sig->sh_old));
     31 
     32         sig->sh_old_max = new_max;
     33         sig->sh_old = p;
     34     }
     35 
     36     /* allocate space for previous handler out of dynamic array */
     37     sig->sh_old[evsignal] = malloc(sizeof *sig->sh_old[evsignal]);  //为数组中的下标为该信号的元素分配内存,在调用_evsignal_set_handler前已经判断过目标为NULL
     38     if (sig->sh_old[evsignal] == NULL) {
     39         event_warn("malloc");
     40         return (-1);
     41     }
     42 
     43     /* save previous handler and setup new handler */
     44 #ifdef HAVE_SIGACTION
     45     memset(&sa, 0, sizeof(sa));
     46     sa.sa_handler = handler;    //这是该信号的回调函数,它已经被定义为通过socket pair发送数据。
     47     sa.sa_flags |= SA_RESTART;   //用于指定信号的处理行为。(SA_RESTART:使被信号打断的系统调用自动重新发起)
     48     sigfillset(&sa.sa_mask);    //获取之前信号屏蔽情况
     49 
     50     if (sigaction(evsignal, &sa, sig->sh_old[evsignal]) == -1) {   //设置新的回调函数及行为,并记录之前的回调函数及行为
     51         event_warn("sigaction");
     52         free(sig->sh_old[evsignal]);
     53         sig->sh_old[evsignal] = NULL;
     54         return (-1);
     55     }
     56 #else
     57     if ((sh = signal(evsignal, handler)) == SIG_ERR) {
     58         event_warn("signal");
     59         free(sig->sh_old[evsignal]);
     60         sig->sh_old[evsignal] = NULL;
     61         return (-1);
     62     }
     63     *sig->sh_old[evsignal] = sh;
     64 #endif
     65 
     66     return (0);
     67 }
     68 
     69 int
     70 evsignal_add(struct event *ev)
     71 {
     72     int evsignal;
     73     struct event_base *base = ev->ev_base;
     74     struct evsignal_info *sig = &ev->ev_base->sig;
     75 
     76     if (ev->ev_events & (EV_READ|EV_WRITE))
     77         event_errx(1, "%s: EV_SIGNAL incompatible use", __func__);
     78     evsignal = EVENT_SIGNAL(ev);    //这个希望被注册信号的值
     79     assert(evsignal >= 0 && evsignal < NSIG);
     80     if (TAILQ_EMPTY(&sig->evsigevents[evsignal])) {  //如果我们还没有注册过该信号,注册它,如果已经注册过,忽略这次注册
     81         event_debug(("%s: %p: changing signal handler", __func__, ev));
     82         if (_evsignal_set_handler(
     83                 base, evsignal, evsignal_handler) == -1)
     84             return (-1);
     85 
     86         /* catch signals if they happen quickly */
     87         evsignal_base = base;    //evsignal_base是一个全局变量,它指向的是我们的信号注册在哪个event_base实例。因此多线程用信号可能冲突,如果2个以上的线程执行到这里,但evsignal_base缺只能指向一个线程中libevent实例.
     88 
     89         if (!sig->ev_signal_added) {    //如果socket pair的读事件没有被注册就注册它,如果已注册不要重复注册(与我们正在注册的信号事件不是同一个)
     90             if (event_add(&sig->ev_signal, NULL))
     91                 return (-1);
     92             sig->ev_signal_added = 1;
     93         }
     94     }
     95 
     96     /* multiple events may listen to the same signal */
     97     TAILQ_INSERT_TAIL(&sig->evsigevents[evsignal], ev, ev_signal_next);  //将这个信号事件插入到信号事件链表末尾,即使因为已注册过而没有注册成功
     98 
     99     return (0);
    100 }
     1 static void
     2 evsignal_handler(int sig)
     3 {
     4     int save_errno = errno;
     5 
     6     if (evsignal_base == NULL) {
     7         event_warn(
     8             "%s: received signal %d, but have no base configured",
     9             __func__, sig);
    10         return;
    11     }
    12 
    13     evsignal_base->sig.evsigcaught[sig]++;    //该信号发生次数加1
    14     evsignal_base->sig.evsignal_caught = 1;    //标记有信号事件发生
    15 
    16 #ifndef HAVE_SIGACTION
    17     signal(sig, evsignal_handler);
    18 #endif
    19 
    20     /* Wake up our notification mechanism */
    21     send(evsignal_base->sig.ev_signal_pair[0], "a", 1, 0);  //socket pair 发送端发送数据
    22     errno = save_errno;
    23 }

    3.当我们event loop过程中收到信号,信号回调函数通过socket pair发送端发送消息,因为我们将socket pair 接收端注册到了epoll监听事件中,epoll_wait将会返回。然后event loop将会调用evsignal_process来调用我们设置信号事件时给定的回调函数

     1 void
     2 evsignal_process(struct event_base *base)
     3 {
     4     struct evsignal_info *sig = &base->sig;
     5     struct event *ev, *next_ev;
     6     sig_atomic_t ncalls;
     7     int i;
     8     
     9     base->sig.evsignal_caught = 0;  //是否有信号事件发生的标记重置为0
    10     for (i = 1; i < NSIG; ++i) {
    11         ncalls = sig->evsigcaught[i];  
    12         if (ncalls == 0)  //如果发生该信号i的次数为0,跳过该信号
    13             continue;
    14         sig->evsigcaught[i] -= ncalls;  //重置该信号发生次数为0
    15 
    16         for (ev = TAILQ_FIRST(&sig->evsigevents[i]);
    17             ev != NULL; ev = next_ev) {    //这里看上去在一个信号上可以注册多个事件,当发生时所有回调都执行一次,但事实上在我们注册的时候每个信号只能注册一个回调
    18             next_ev = TAILQ_NEXT(ev, ev_signal_next);
    19             if (!(ev->ev_events & EV_PERSIST))  //如果不是持久化事件,注销这个事件
    20                 event_del(ev);
    21             event_active(ev, EV_SIGNAL, ncalls);  //插入到已激活列表中,执行次数为信号发生次数
    22         }
    23 
    24     }
    25 }

    4.最后,我们再来看看如何注销信号事件的

     1 int
     2 evsignal_del(struct event *ev)
     3 {
     4     struct event_base *base = ev->ev_base;
     5     struct evsignal_info *sig = &base->sig;
     6     int evsignal = EVENT_SIGNAL(ev);   //先取得要注销信号事件的信号
     7 
     8     assert(evsignal >= 0 && evsignal < NSIG);
     9 
    10     /* multiple events may listen to the same signal */
    11     TAILQ_REMOVE(&sig->evsigevents[evsignal], ev, ev_signal_next);  
    12 
    13     if (!TAILQ_EMPTY(&sig->evsigevents[evsignal]))
    14         return (0);
    15 
    16     event_debug(("%s: %p: restoring signal handler", __func__, ev));
    17 
    18     return (_evsignal_restore_handler(ev->ev_base, EVENT_SIGNAL(ev)));  //注销信号事件后将信号的回调函数及处理行为等恢复
    19 }
  • 相关阅读:
    【动态规划、贪心】剪绳子
    【Leetcode 数组】 有序数组中出现次数超过25%的元素(1287)
    【Leetcode 数组】 杨辉三角(118)
    【Leetcode 数组】 螺旋矩阵 II(59)
    【Leetcode 数组】 螺旋矩阵(54)
    【BFPRT】数组中出现次数超过一半的数字
    【Leetcode 大小堆、二分、BFPRT、二叉排序树、AVL】数据流的中位数(295)
    【Leetcode 二分】 滑动窗口中位数(480)
    常见ie9兼容问题
    js常用正则表达式
  • 原文地址:https://www.cnblogs.com/dyan1024/p/10003540.html
Copyright © 2011-2022 走看看