libevent原始碼分析(六)
libevent-1.4/sample/singnal-test.c
接下來看看 event_base_dispatch(base);
這個函式是整個Reactor的核心,是一個loop.
函式定義:
int event_base_dispatch(struct event_base *event_base)
{
return (event_base_loop(event_base, 0));
}
event_base_dispatch呼叫event_base_loop:
int event_base_loop(struct event_base *base, int flags) { const struct eventop *evsel = base->evsel; void *evbase = base->evbase; struct timeval tv; struct timeval *tv_p; int res, done; /* clear time cache */ base->tv_cache.tv_sec = 0; if (base->sig.ev_signal_added) evsignal_base = base; done = 0; while (!done) { /* Terminate the loop if we have been asked to */ if (base->event_gotterm) { base->event_gotterm = 0; break; } if (base->event_break) { base->event_break = 0; break; } /* You cannot use this interface for multi-threaded apps */ while (event_gotsig) { event_gotsig = 0; if (event_sigcb) { res = (*event_sigcb)(); if (res == -1) { errno = EINTR; return (-1); } } } timeout_correct(base, &tv); tv_p = &tv; if (!base->event_count_active && !(flags & EVLOOP_NONBLOCK)) { timeout_next(base, &tv_p); } else { /* * if we have active events, we just poll new events * without waiting. */ evutil_timerclear(&tv); } /* If we have no events, we just exit */ if (!event_haveevents(base)) { event_debug(("%s: no events registered.", __func__)); return (1); } /* update last old time */ gettime(base, &base->event_tv); /* clear time cache */ base->tv_cache.tv_sec = 0; res = evsel->dispatch(base, evbase, tv_p); if (res == -1) return (-1); gettime(base, &base->tv_cache); timeout_process(base); if (base->event_count_active) { event_process_active(base); if (!base->event_count_active && (flags & EVLOOP_ONCE)) done = 1; } else if (flags & EVLOOP_NONBLOCK) done = 1; } /* clear time cache */ base->tv_cache.tv_sec = 0; event_debug(("%s: asked to terminate loop.", __func__)); return (0); }
可以看到event_base_loop基本上就是一個while迴圈,不斷的處理訊號事件、定時事件以及監聽事件,從程式流程來看,訊號、定時以及監聽事件分別獨立處理。
通過註釋也可以看到,訊號事件的處理不能在多執行緒中使用,原因是使用了全域性變數event_gotsig、event_sigcb,但其實我們註冊的訊號SIGINT函式不在此處處理,可以去看看event_add函式,也是就上篇文章l ibevent原始碼分析(五)。在上篇中曾說過當訊號被捕獲到時,訊號響應函式向ev_signal_pair[0]寫資料,從而導致ev_signal_pair[1]讀事件就緒,從而喚醒event_base_dispatch中的監聽loop,喚醒部分在程式碼
evsel->dispatch(base, evbase, tv_p)——epoll_dispatch
——epoll_wait(epollop->epfd, events, epollop->nevents, timeout)
epoll_wait系統呼叫就是監聽的事件就緒以後會返回,是否阻塞取決於timeout的值,有定時事件時就超時,沒有就阻塞。
可以看到epoll_wait返回後首先處理的就是訊號,可以是喚醒的被正常註冊的訊號,也可以是被訊號影響而直接返回。
sig.evsignal_caught訊號捕獲標誌在
evsignal_process(base)函式中被清空,計數sig->evsigcaught[SIGINT]也被使用到了,訊號事件被插入到啟用佇列
event_active(ev, EV_SIGNAL, ncalls)——event_queue_insert(ev->ev_base, ev, EVLIST_ACTIVE);//base->event_count_active++;
返回event_base_loop函式看關於base->event_count_active部分的處理:
if (base->event_count_active) {
event_process_active(base);
if (!base->event_count_active && (flags & EVLOOP_ONCE))
done = 1;
}
在SIGINT事件插入啟用列表時base->event_count_active++,所以會繼續處理event_process_active(base):
static void event_process_active(struct event_base *base) { struct event *ev; struct event_list *activeq = NULL; int i; short ncalls; for (i = 0; i < base->nactivequeues; ++i) { if (TAILQ_FIRST(base->activequeues[i]) != NULL) { activeq = base->activequeues[i]; break; } } for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) { if (ev->ev_events & EV_PERSIST) event_queue_remove(base, ev, EVLIST_ACTIVE); else event_del(ev); /* Allows deletes to work */ ncalls = ev->ev_ncalls; ev->ev_pncalls = &ncalls; while (ncalls) { ncalls--; ev->ev_ncalls = ncalls; (*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg); if (event_gotsig || base->event_break) { ev->ev_pncalls = NULL; return; } } ev->ev_pncalls = NULL; } }
遍歷啟用佇列,處理相應的事件回撥函式。其中
ncalls就是在訊號函式被處理時刻對應的計數sig->evsigcaught[SIGINT]的值,
ev來源於
for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq))
那麼activeq又是來源於base->activequeues[i],看下函式流程,關於base->activequeues[i]部分:
event_base_dispatch(base);
event_base_loop(base, 0);
evsel->dispatch(base, base->evbase, tv_p)//沒有定時事件,tv_p=NULL
epoll_dispatch(base, base->evbase, tv_p)
evsignal_process(base)
其中,在evsignal_process(base)中:
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);
}
}
sig->evsigevents[i])來源與
event_add(&signal_int, NULL);//signal-test.c
evsel->add(base->evsel, (&signal_int);
evsignal_add(&signal_int);
TAILQ_INSERT_TAIL(&sig->evsigevents[SIGINT], (&signal_int, ev_signal_next);
event_active——event_queue_insert(ev->ev_base, ev, EVLIST_ACTIVE);
case EVLIST_ACTIVE:
base->event_count_active++;
TAILQ_INSERT_TAIL(base->activequeues[ev->ev_pri],
ev,ev_active_next);
break;
整個都連起來了,很容易知道 for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq))中的ev直接指向signal_int,那麼以下部分就很明白了:
(*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg);
直接替換為
signal_cb((int)ev->ev_fd,ev->ev_res,ev->ev_arg)——signal_cb(SIGINT,EV_SIGNAL|EV_PERSIST, &signal_int)
至此,SIGINT的註冊的訊號事件回撥函式開始執行。