libevent 阅读记录三

时间:2023-01-01 00:18:06

前面我们对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 }