程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> libevent源碼淺析(四)

libevent源碼淺析(四)

編輯:關於C++

最近剛剛一個項目自己用libevent,因此這幾天又把libevent的代碼拿出來翻了下,當初看的時候有些似是而非的東西,這次是基本沒有了。這篇也算是前面幾篇libevent的blog的補充了。

struct event_base {
 const struct eventop *evsel;
 void *evbase;
 int event_count; /* counts number of total events */
 int event_count_active; /* counts number of active events */

 int event_gotterm; /* Set to terminate loop */
 int event_break; /* Set to terminate loop immediately */

 /* active event management */
 struct event_list **activequeues;
 int nactivequeues;

 /* signal handling info */
 struct evsignal_info sig;

 struct event_list eventqueue;
 struct timeval event_tv;

 struct min_heap timeheap;

 struct timeval tv_cache;
};

我們這裡用select來講,其他的事件驅動器都相似。

我們來看,其中activequeues我們知道是表示激活的事件隊列.這裡libevent的處理是,select被喚醒後,調用 event_active方法,將此事件插入到activequeues隊列中,這裡這個隊列的實現是用tail queue。然後libevent會執行event_process_active方法,從而從激活隊列中,依次執行所激活的事件。這裡這個隊列之所以是一個指針的指針,是因為,libevent中事件還分為優先級,這樣每個優先級都有一個activequeues隊列。

記下來我們再來看定時器的實現,libevent會在每次執行循環時,從優先級隊列中取出來最小的那個時間,然後將它加入到select中,從而實現定時器。而在每次select超時退出後,libevent會從小到大取出超時時間(直到大於當前時間),每次和當前時間比較,如果已超時,則會從優先級隊列中刪除此節點,然後將此超時事件加入到激活隊列中。

接下來我們來看相關代碼。

int
event_base_loop(struct event_base *base, int flags)
{
..................................................

 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);
 }

.........................................................
 /* clear time cache */
 base->tv_cache.tv_sec = 0;
///調用相關事件驅動引擎的dispatch方法,這個方法中會將已激活的事件加入到激活隊列,這裡看到tv_p也就是上面取到的超時時間被傳入到dispatch。
 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);
}

來看timeout_next方法

static int
timeout_next(struct event_base *base, struct timeval **tv_p)
{
 struct timeval now;
 struct event *ev;
 struct timeval *tv = *tv_p;
///取出最小的那個時間。
 if ((ev = min_heap_top(&base->timeheap)) == NULL) {
 /* if no time-based events are active wait for I/O */
 *tv_p = NULL;
 return (0);
 }
///得到當前的時間。
 if (gettime(base, &now) == -1)
 return (-1);
///已超時則直接退出
 if (evutil_timercmp(&ev->ev_timeout, &now, <=)) {
 evutil_timerclear(tv);
 return (0);
 }
///將定時器事件減去當前時間,也就是超時時間付給tv。
 evutil_timersub(&ev->ev_timeout, &now, tv);

 assert(tv->tv_sec >= 0);
 assert(tv->tv_usec >= 0);

 event_debug(("timeout_next: in %ld seconds", tv->tv_sec));
 return (0);
}

void
timeout_process(struct event_base *base)
{
..............................................

 gettime(base, &now);
///開始遍歷此優先級隊列
 while ((ev = min_heap_top(&base->timeheap))) {
///如果比當前時間大,則說明還沒到超時時間因此直接退出。
 if (evutil_timercmp(&ev->ev_timeout, &now, >))
  break;
///刪除此超時事件,因此我們在使用定時器時,需要我們每次進入定時器後,再次add此事件。
 /* delete this event from the I/O queues */
 event_del(ev);

 event_debug(("timeout_process: call %p",
  ev->ev_callback));
///加入激活隊列。
 event_active(ev, EV_TIMEOUT, 1);
 }
}

其中event_active是通過event_queue_insert來插入到激活隊列的,因此我們來看這個函數:

void
event_queue_insert(struct event_base *base, struct event *ev, int queue)
{
..............................................
 ev->ev_flags |= queue;
 switch (queue) {
///這個主要用來保存所有的激活以及非激活隊列,也就是eventqueue.
 case EVLIST_INSERTED: 
 TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next);
 break;
 case EVLIST_ACTIVE: 
///激活隊列數加一,並將此事件插入到相應的優先級的激活隊列中。
 base->event_count_active++;
 TAILQ_INSERT_TAIL(base->activequeues[ev->ev_pri],
   ev,ev_active_next);
 break;
 case EVLIST_TIMEOUT: {
///處理超時事件。
 min_heap_push(&base->timeheap, ev);
 break;
 }
 default: 
 event_errx(1, "%s: unknown queue %x", __func__, queue);
 }
}

最後來看執行激活隊列

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;
 }
 }

 assert(activeq != NULL);

///開始遍歷上面取出的隊列
 for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) {
 if (ev->ev_events & EV_PERSIST)
///如果有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)
  return;
 }
 }
}

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved