版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Function_Dou/article/details/87904134
上节咱们分析了信号的初始化以及最重要的socket pair
以及使用原因. 本节继续来分析event
信号的注册和激活功能, 其实上一节最后分析socket pair
已经提到了一些.
信号注册
信号调用evsignal_add
函数来实现信号的注册, 有点向event
调用event_add
函数.
int
evsignal_add(struct event *ev)
{
int evsignal;
struct event_base *base = ev->ev_base;
struct evsignal_info *sig = &ev->ev_base->sig;
// 该事件是信号事件并不是读写事件
if (ev->ev_events & (EV_READ|EV_WRITE))
event_errx(1, "%s: EV_SIGNAL incompatible use", __func__);
// 获取信号监听值
evsignal = EVENT_SIGNAL(ev);
assert(evsignal >= 0 && evsignal < NSIG);
// 该信号队列为空. 实质还没有将该信号加入到信号链表中
if (TAILQ_EMPTY(&sig->evsigevents[evsignal])) {
event_debug(("%s: %p: changing signal handler", __func__, ev));
// 因为该信号还没被注册回调函数, 所以这里是设置信号捕捉函数
if (_evsignal_set_handler(
base, evsignal, evsignal_handler) == -1)
return (-1);
/* catch signals if they happen quickly */
evsignal_base = base;
// 信号没有被加入到注册队列中, 则添加到注册队列中.
if (!sig->ev_signal_added) {
if (event_add(&sig->ev_signal, NULL))
return (-1);
sig->ev_signal_added = 1;
}
}
/* multiple events may listen to the same signal */
// 将信号加入到其信号监听的队列中
TAILQ_INSERT_TAIL(&sig->evsigevents[evsignal], ev, ev_signal_next);
return (0);
}
- 很明显这是信号的注册函数, 所以如果是IO操作肯定返回出错
- 查看该信号的监听队列是否已经被初始化过, 如果没有就将它初始化并且设置信号回调的默认回调函数
evsignal_handler
. 如果信号也没有加入到注册队列中, 则加入到注册队列中(一种信号只注册一次), 并设置ev_signal_added
标志位, 表示加入注册队列中. - 最后将信号加入信号队列中
_evsignal_set_handler 函数
该函数就是很简单的通过signal
函数注册信号的回调函数, 默认设置的回调函数都是evsignal_handler
int
_evsignal_set_handler(struct event_base *base,
int evsignal, void (*handler)(int))
{
#ifdef HAVE_SIGACTION
struct sigaction sa;
#else
ev_sighandler_t sh;
#endif
struct evsignal_info *sig = &base->sig;
void *p;
/*
* resize saved signal handler array up to the highest signal number.
* a dynamic array is used to keep footprint on the low side.
*/
if (evsignal >= sig->sh_old_max) {
int new_max = evsignal + 1;
event_debug(("%s: evsignal (%d) >= sh_old_max (%d), resizing",
__func__, evsignal, sig->sh_old_max));
// 为该信号链表分配空间
p = realloc(sig->sh_old, new_max * sizeof(*sig->sh_old));
if (p == NULL) {
event_warn("realloc");
return (-1);
}
// 初始化为 0
memset((char *)p + sig->sh_old_max * sizeof(*sig->sh_old),
0, (new_max - sig->sh_old_max) * sizeof(*sig->sh_old));
sig->sh_old_max = new_max;
sig->sh_old = p;
}
/* allocate space for previous handler out of dynamic array */
sig->sh_old[evsignal] = malloc(sizeof *sig->sh_old[evsignal]);
if (sig->sh_old[evsignal] == NULL) {
event_warn("malloc");
return (-1);
}
/* save previous handler and setup new handler */
#ifdef HAVE_SIGACTION
memset(&sa, 0, sizeof(sa));
sa.sa_handler = handler; // 设置回调函数
sa.sa_flags |= SA_RESTART; // 设置标志位
sigfillset(&sa.sa_mask);
if (sigaction(evsignal, &sa, sig->sh_old[evsignal]) == -1) {
event_warn("sigaction");
free(sig->sh_old[evsignal]);
sig->sh_old[evsignal] = NULL;
return (-1);
}
#else
if ((sh = signal(evsignal, handler)) == SIG_ERR) {
event_warn("signal");
free(sig->sh_old[evsignal]);
sig->sh_old[evsignal] = NULL;
return (-1);
}
*sig->sh_old[evsignal] = sh;
#endif
return (0);
}
evsignal_handler 函数
// 信号注册的默认回调函数
// 回调函数其实就是发送一字节. 实质是通知该处理信号了
static void
evsignal_handler(int sig)
{
int save_errno = errno;
if (evsignal_base == NULL) {
event_warn(
"%s: received signal %d, but have no base configured",
__func__, sig);
return;
}
evsignal_base->sig.evsigcaught[sig]++; // 信号被捕捉的次数
evsignal_base->sig.evsignal_caught = 1; // 表示捕捉到信号
#ifndef HAVE_SIGACTION
signal(sig, evsignal_handler);
#endif
/* Wake up our notification mechanism */
// 回调函数其实就是发送一字节. 实质是通知该处理信号了
// 唤醒 event_loop 主循环处理信号
send(evsignal_base->sig.ev_signal_pair[0], "a", 1, 0);
errno = save_errno;
}
设置信号的捕捉次数以及捕捉标志位. 这里我再将上节话搬过来
当信号发生时, 注册的回调函数evsignal_handler
会向服务端发送一字节数据并设置该信号标志和次数, 而服务端则调用统一的就绪回调函数evsignal_cb
接收一字节数据, 而libevent通过分发器(如 : epoll 中的epoll_wait) 调用evsignal_process
将信号加入就绪队列中.
信号激活
evsignal_process
其实就是直接将信号从就绪队列中删除在加入到就绪队列中. 功能现在看来很简单. 其实除去信号的部分成员参数需要修改, 就像是直接调用event_active
函数.
// 激活事件
void
evsignal_process(struct event_base *base)
{
struct evsignal_info *sig = &base->sig;
struct event *ev, *next_ev;
sig_atomic_t ncalls;
int i;
// 清空捕捉信号标志位
base->sig.evsignal_caught = 0;
for (i = 1; i < NSIG; ++i) {
ncalls = sig->evsigcaught[i];
if (ncalls == 0)
continue;
sig->evsigcaught[i] -= ncalls;
for (ev = TAILQ_FIRST(&sig->evsigevents[i]);
ev != NULL; ev = next_ev) {
next_ev = TAILQ_NEXT(ev, ev_signal_next);
// 如果事件不是永久事件, 则激活后就删除该事件
if (!(ev->ev_events & EV_PERSIST))
event_del(ev);
// 激活事件, 将信号事件加入到信号队列中, 并标记信号的次数
event_active(ev, EV_SIGNAL, ncalls);
}
}
}
总结
本节分析了信号设置默认回调函数还有信号是通过evsignal_process
激活的.