libevent的初衷就是設(shè)計一個跨平臺的輕量級I/0框架,由于歷史問題,各平臺的I/O復(fù)用機(jī)制難以統(tǒng)一。因此,這部分處理跨平臺的方法值得重點(diǎn)關(guān)注。
eventop在源碼中定義如下:

static const struct eventop *eventops[]=
{

#ifdef HAVE_EVENT_PORTS

&evportops,

#endif

….

}

由此可見libevent通過宏來在編譯期找出可用的復(fù)用機(jī)制。
其中的順序也是大文章的。官方的文檔中說明libevent中支持的復(fù)用機(jī)制 /dev/poll, kqueue(2), event ports, select(2), poll(2) and epoll(4).
libevent開發(fā)人員通過對各種機(jī)制的基準(zhǔn)測試,根據(jù)性能高到低選擇復(fù)用機(jī)制優(yōu)先順序如圖所示:

從中也可以了解到不同平臺機(jī)制的不統(tǒng)一。標(biāo)準(zhǔn)的 poll和 select卻難以滿足大規(guī)模架構(gòu)的需要,具體可以參考Dan Kegel的 "The C10K problem"文檔。
關(guān)于機(jī)制的采用,libevent采用的是函數(shù)指針的方法。

struct eventop
{

const char *name; /**//*機(jī)制名稱*/

void *(*init)(struct event_base *); /**//*初始化事件*/

int (*add)(void *, struct event *); /**//*添加事件*/

int (*del)(void *, struct event *); /**//* 刪除事件*/

int (*dispatch)(struct event_base *, void *, struct timeval *) /**//* 調(diào)度事件 */

void (*dealloc)(struct event_base *, void *);/**//* 釋放資源*/
int need_reinit;
};

每個eventop即對應(yīng)一種IO復(fù)用機(jī)制,其中的每個函數(shù)指針都指向使用該機(jī)制對事件進(jìn)行操作的方法。
比如對應(yīng)epoll的eventop結(jié)構(gòu)中:
1.void *(*init)(…)函數(shù)指針對應(yīng)的是static void * epoll_init(…)
2.在epoll_init()里,首先對環(huán)境變量進(jìn)行檢測,發(fā)現(xiàn)沒有epoll機(jī)制時立即返回NULL。
3.使用epoll_create(32000)指定了連接數(shù)目的上限為32000個,然后對epollop的各個成員所需資源進(jìn)行分配。
4.最后調(diào)用libevent自身的信號初始化函數(shù)。
選擇機(jī)制并將其初始化的過程十分簡單:

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

base->evsel = eventops[i];

base->evbase = base->evsel->init(base);

}


遍歷存儲機(jī)制的eventops數(shù)組,按順序依次嘗試初始化,一種機(jī)制被成功初始化則立即跳出循環(huán)。當(dāng)然,檢測系統(tǒng)環(huán)境可用機(jī)制,選擇哪種機(jī)制更合適,具體的復(fù)用機(jī)制如何使用,這一切的瑣碎細(xì)節(jié)你都無需關(guān)心,使用時,只要調(diào)用event_init()函數(shù)即可。Libevent對各種復(fù)用機(jī)制的巧妙封裝避免了開發(fā)者開發(fā)大規(guī)模架構(gòu)時,處理跨平臺時機(jī)制選擇的苦惱。