1. 程式人生 > >libevent原始碼分析(二)

libevent原始碼分析(二)

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(管道)。