前言
在本節中,我們將初次接觸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
:執行激活的事件