(四)初探反應器(event_base)

前言

在本節中,我們將初次接觸struct event_base,並介紹下event_init函數。由於event_base屬於整個libevent中中心的部位,所以我們沒辦法一下就完全弄懂它,需要結合後面的各部分知識才能有一個很好的認識。

struct event_base

還記得我們之前寫的那個小例子嗎,一開始調用了event_init()函數。它到底有什麼作用?對應函數原型在libevent庫中的event.c文件中。

struct event_base *
event_init(void)
{
  struct event_base *base = event_base_new();
  if (base != NULL)
    current_base = base;
  return (base);
}

它的返回值是struct event_base *,並且第一個局部變量也是該類型的。看來我們先要了解一下struct event_base結構體的內容,才能進一步往下走。該結構體的定義在event-internal.h中。

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;
};
  • const struct eventop *evsel
    :這裏又出來一個新的結構體,它就是用來封裝多種I/O複用機制的結構體,後面我們會詳細討論
  • void *evbase:它用於組織某種具體的I/O複用機制需要的成員(其實是一種結構體,不過之所以命令成void *,是爲了支持多種機制,因爲不同的機制取名不同,epoll中對應struct epollop)
  • event_count:註冊的事件總數
  • event_count_active:激活的事件總數
  • event_gotterm:一個標識位,如果置爲1了,那麼就會中斷事件主循環,爲0則不會。可能你現在還不太清楚事件主循環是什麼,不過不要緊。你先知道有這個東西就行了。
  • event_break:一個標識位,和上面的功能類似,置爲0了就會中斷循環。不過導致它們不一樣的是兩個函數event_base_loopexit()event_base_loopbreak(),這個我們放在事件主循環那一節講。
  • struct event_list **activequeues:它用來按優先級管理已經激活(就緒)的事件。一級指針指向不同優先級,二級指針指向具體的事件,並形成鏈表結構。如優先級爲1的事件a,優先級爲1的事件b。
  • struct evsignal_info sig:管理信號相關的成員的結構體,放在後面講信號的時候再講。
  • struct event_list eventqueue:保存已註冊事件event的指針
  • struct min_heap timeheap:管理定時事件的小根堆,放在後面講關於定時的時候再來討論。
  • 最後兩個也是跟定時有關,放在後面講。

設置event demultiplexer

接下來event_init調用了event_base_new()函數,返回了一個struct event_base *,並且可以看到current_base是一個全局變量,用於保存當前的反應器。event_base_new完成的工作無非是各種初始化,比如對信號以及定時事件等,這些現在說沒什麼意義,不過有一點可以提一下。那就是不知道你發現沒有,我們起初並沒有設置過我們要採用哪種I/O多路複用機制,而是libevent決定的。
event_base_new中設置使用哪種I/O多路複用機制的代碼如下:

for(i = 0; eventops[i] && !base->evbase; i++)
{
  base->evsel = eventops[i];
  base->evbase = base->evsel->init(base);
}

你可能看不太懂,不過沒關係。struct eventops數組存放了每一個支持的多路I/O複用機制。依照這個代碼邏輯我們可以大致猜到,它是遍歷eventops數組和檢查base->evbase是否爲空來查看哪個多路I/O機制可用就決定使用哪個。而evsel相當於指向了某個具體的多路I/O複用機制,並且它調用了init函數初始化了該多路I/O複用機制。拿epoll來舉例,init調用的就是epoll_create之類的函數。

關於反應器的接口函數

上面介紹了一下struct event_base,但是關於libevent的核心,反應器是如何註冊事件以及刪除事件,還有執行事件循環的卻並沒有提到,由於這些函數涉及到了事件處理器、信號處理、還有定時事件等,所以我打算先把事件處理器講了再回過頭來看看這些接口函數。
主要接口如下:

int event_add(struct event *ev, const struct timeval *timeout);
int event_del(struct event *ev);
int event_base_loop(struct event_base *base, int loops);
void event_active(struct event *event, int res, short events);
void event_process_active(struct event_base *base);

這裏先把作用告知,好有一個大體的印象。
event_add:註冊事件
event_del:刪除事件
event_base_loop:執行事件主循環(即等待註冊的事件激活並處理)
event_active:激活事件
event_process_active:執行激活的事件

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章