libevent原始碼分析(二)
阿新 • • 發佈:2019-01-27
libevet——Reactor初始化,先來看看event_base_new的定義,先關注主線程式碼,後續研究細節:
struct event_base *event_base_new(void) { int i; struct event_base *base; if ((base = calloc(1, sizeof(struct event_base))) == NULL) event_err(1, "%s: calloc", __func__); event_sigcb = NULL; event_gotsig = 0; detect_monotonic(); gettime(base, &base->event_tv); min_heap_ctor(&base->timeheap); TAILQ_INIT(&base->eventqueue); base->sig.ev_signal_pair[0] = -1; base->sig.ev_signal_pair[1] = -1; base->evbase = NULL; for (i = 0; eventops[i] && !base->evbase; i++) { base->evsel = eventops[i]; base->evbase = base->evsel->init(base); } if (base->evbase == NULL) event_errx(1, "%s: no event mechanism available", __func__); if (evutil_getenv("EVENT_SHOW_METHOD")) event_msgx("libevent using: %s\n", base->evsel->name); /* allocate a single active event queue */ event_base_priority_init(base, 1); return (base); }
此程式碼主要例項化一個 struct event_base 物件base, 注意初始化了一個最小堆結構以及訊號處理相關的資源,base->sig.ev_signal_pair[0]和base->sig.ev_signal_pair[1]是socketpair用來線上程間進行訊號處理事件的喚醒動作。需要關注的是
for (i = 0; eventops[i] && !base->evbase; i++) { base->evsel = eventops[i]; base->evbase = base->evsel->init(base); }
這是核心部分,定義如下
static const struct eventop *eventops[] = { #ifdef HAVE_EVENT_PORTS &evportops, #endif #ifdef HAVE_WORKING_KQUEUE &kqops, #endif #ifdef HAVE_EPOLL &epollops, #endif #ifdef HAVE_DEVPOLL &devpollops, #endif #ifdef HAVE_POLL &pollops, #endif #ifdef HAVE_SELECT &selectops, #endif #ifdef WIN32 &win32ops, #endif NULL };
目前,預設巨集定義HAVE_EPOLL,故
base->evsel = &epollops
下面來看看epollops 的定義:
const struct eventop epollops = {
"epoll",
epoll_init,
epoll_add,
epoll_del,
epoll_dispatch,
epoll_dealloc,
1 /* need reinit */
};
也就是說 base->evbase = base->evsel->init(base)直接就表達 epoll_init(base),那麼我們繼續沿著epoll_init看下去:
static void *epoll_init(struct event_base *base)
{
int epfd;
struct epollop *epollop;
/* Disable epollueue when this environment variable is set */
if (evutil_getenv("EVENT_NOEPOLL"))
return (NULL);
/* Initalize the kernel queue */
if ((epfd = epoll_create(32000)) == -1) {
if (errno != ENOSYS)
event_warn("epoll_create");
return (NULL);
}
FD_CLOSEONEXEC(epfd);
if (!(epollop = calloc(1, sizeof(struct epollop))))
return (NULL);
epollop->epfd = epfd;
/* Initalize fields */
epollop->events = malloc(INITIAL_NEVENTS * sizeof(struct epoll_event));
if (epollop->events == NULL) {
free(epollop);
return (NULL);
}
epollop->nevents = INITIAL_NEVENTS;
epollop->fds = calloc(INITIAL_NFILES, sizeof(struct evepoll));
if (epollop->fds == NULL) {
free(epollop->events);
free(epollop);
return (NULL);
}
epollop->nfds = INITIAL_NFILES;
evsignal_init(base);
return (epollop);
}
其中epfd = epoll_create(32000)有沒有覺得很熟悉,建立epoll控制代碼,如果使用過epoll進行程式設計並且熟悉epoll相關介面的讀者看到這個應該大概能夠知道 libevent的Reactor實現原理。
evsignal_init(base)函式註釋對於socketpair的使用很清楚,非常有必要去研究一下,這個socketpair的用法可用於本機的程序間通訊,有類似功能的還有pipe(管道)。