翻譯:libevent參考手冊第二章:創建event_base (四) (轉)
Posted on 2013-02-04 17:18 鑫龍 閱讀(3880) 評論(0) 編輯 收藏 引用 所屬分類: LIBEVENT譯自http://www.wangafu.net/~nickm/libevent-book/Ref2_eventbase.html
轉自:http://blog.sina.com.cn/s/blog_56dee71a0100qdxx.html
使用libevent函數之前需要分配一個或者多個event_base結構體。每個event_base結構體持有一個事件集合,可以檢測以確定哪個事件是激活的。
如果設置event_base使用鎖,則可以安全地在多個線程中訪問它。然而,其事件循環只能運行在一個線程中。如果需要用多個線程檢測IO,則需要為每個線程使用一個event_base。
每個event_base都有一種用于檢測哪種事件已經就緒的“方法”,或者說后端??梢宰R別的方法有:
v select
v poll
v epoll
v kqueue
v devpoll
v evport
v win32
用戶可以用環境變量禁止某些特定的后端。比如說,要禁止kqueue后端,可以設置EVENT_NOKQUEUE環境變量。如果要用編程的方法禁止后端,請看下面關于event_config_avoid_method()的說明。
1 建立默認的event_base
event_base_new()函數分配并且返回一個新的具有默認設置的event_base。函數會檢測環境變量,返回一個到event_base的指針。如果發生錯誤,則返回NULL。選擇各種方法時,函數會選擇OS支持的最快方法。
接口這個函數當前僅在Windows上使用IOCP時有用,雖然將來可能在其他平臺上有用。這個函數告訴event_config在生成多線程event_base的時候,應該試圖使用給定數目的CPU。注意這僅僅是一個提示:event_base使用的CPU可能比你選擇的要少。
示例
struct event_base *event_base_new(void);
大多數程序使用這個函數就夠了。
event_base_new()函數聲明在<event2/event.h>中,首次出現在libevent 1.4.3版。
2 創建復雜的event_base
要對取得什么類型的event_base有更多的控制,就需要使用event_config。event_config是一個容納event_base配置信息的不透明結構體。需要event_base時,將event_config傳遞給event_base_new_with_config()。
2.1 接口
struct event_config *event_config_new(void);
struct event_base *event_base_new_with_config(const struct event_config *cfg);
void event_config_free(struct event_config *cfg);要使用這些函數分配event_base,先調用event_config_new()分配一個event_config。然后,對event_config調用其它函數,設置所需要的event_base特征。最后,調用event_base_new_with_config()獲取新的event_base。完成工作后,使用event_config_free()釋放event_config。
2.2 接口
int event_config_avoid_method(struct event_config *cfg, const char *method);
enum event_method_feature {
EV_FEATURE_ET = 0x01,
EV_FEATURE_O1 = 0x02,
EV_FEATURE_FDS = 0x04,
};
int event_config_require_features(struct event_config *cfg,
enum event_method_feature feature);
enum event_base_config_flag {
EVENT_BASE_FLAG_NOLOCK = 0x01,
EVENT_BASE_FLAG_IGNORE_ENV = 0x02,
EVENT_BASE_FLAG_STARTUP_IOCP = 0x04,
EVENT_BASE_FLAG_NO_CACHE_TIME = 0x08,
EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST = 0x10,
EVENT_BASE_FLAG_PRECISE_TIMER = 0x20
};
int event_config_set_flag(struct event_config *cfg,
enum event_base_config_flag flag);調用event_config_avoid_method()可以通過名字讓libevent避免使用特定的可用后端。調用event_config_require_feature()讓libevent不使用不能提供所有指定特征的后端。調用event_config_set_flag()讓libevent在創建event_base時設置一個或者多個將在下面介紹的運行時標志。
event_config_require_features()可識別的特征值有:
v EV_FEATURE_ET:要求支持邊沿觸發的后端
v EV_FEATURE_O1:要求添加、刪除單個事件,或者確定哪個事件激活的操作是O(1)復雜度的后端
v EV_FEATURE_FDS:要求支持任意文件描述符,而不僅僅是套接字的后端
event_config_set_flag()可識別的選項值有:
v EVENT_BASE_FLAG_NOLOCK:不要為event_base分配鎖。設置這個選項可以為event_base節省一點用于鎖定和解鎖的時間,但是讓在多個線程中訪問event_base成為不安全的。
v EVENT_BASE_FLAG_IGNORE_ENV:選擇使用的后端時,不要檢測EVENT_*環境變量。使用這個標志需要三思:這會讓用戶更難調試你的程序與libevent的交互。
v EVENT_BASE_FLAG_STARTUP_IOCP:僅用于Windows,讓libevent在啟動時就啟用任何必需的IOCP分發邏輯,而不是按需啟用。
v EVENT_BASE_FLAG_NO_CACHE_TIME:不是在事件循環每次準備執行超時回調時檢測當前時間,而是在每次超時回調后進行檢測。注意:這會消耗更多的CPU時間。
v EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST:告訴libevent,如果決定使用epoll后端,可以安全地使用更快的基于changelist的后端。epoll-changelist后端可以在后端的分發函數調用之間,同樣的fd多次修改其狀態的情況下,避免不必要的系統調用。但是如果傳遞任何使用dup()或者其變體克隆的fd給libevent,epoll-changelist后端會觸發一個內核bug,導致不正確的結果。在不使用epoll后端的情況下,這個標志是沒有效果的。也可以通過設置EVENT_EPOLL_USE_CHANGELIST環境變量來打開epoll-changelist選項。
上述操作event_config的函數都在成功時返回0,失敗時返回-1。
注意
設置event_config,請求OS不能提供的后端是很容易的。比如說,對于libevent 2.0.1-alpha,在Windows中是沒有O(1)后端的;在Linux中也沒有同時提供EV_FEATURE_FDS和EV_FEATURE_O1特征的后端。如果創建了libevent不能滿足的配置,event_base_new_with_config()會返回NULL。
2.3 接口
int event_config_set_num_cpus_hint(struct event_config *cfg, int cpus)這個函數當前僅在Windows上使用IOCP時有用,雖然將來可能在其他平臺上有用。這個函數告訴event_config在生成多線程event_base的時候,應該試圖使用給定數目的CPU。注意這僅僅是一個提示:event_base使用的CPU可能比你選擇的要少。
示例
struct event_config *cfg;
struct event_base *base;
int i;
/* My program wants to use edge-triggered events if at all possible. So
I'll try to get a base twice: Once insisting on edge-triggered IO, and
once not. */
for (i=0; i<2; ++i) {
cfg = event_config_new();
/* I don't like select. */
event_config_avoid_method(cfg, "select");
if (i == 0)
event_config_require_features(cfg, EV_FEATURE_ET);
base = event_base_new_with_config(cfg);
event_config_free(cfg);
if (base)
break;
/* If we get here, event_base_new_with_config() returned NULL. If
this is the first time around the loop, we'll try again without
setting EV_FEATURE_ET. If this is the second time around the
loop, we'll give up. */
}這些函數和類型在<event2/event.h>中聲明。
EVENT_BASE_FLAG_IGNORE_ENV標志首次出現在2.0.2-alpha版本。event_config_set_num_cpus_hint()函數是2.0.7-rc版本新引入的。本節的其他內容首次出現在2.0.1-alpha版本。
3 檢查event_base的后端方法
有時候需要檢查event_base支持哪些特征,或者當前使用哪種方法。
3.1 接口
const char **event_get_supported_methods(void);event_get_supported_methods()函數返回一個指針,指向libevent支持的方法名字數組。這個數組的最后一個元素是NULL。
示例
int i;
const char **methods = event_get_supported_methods();
printf("Starting Libevent %s. Available methods are:\n",
event_get_version());
for (i=0; methods[i] != NULL; ++i) {
printf(" %s\n", methods[i]);
}注意
這個函數返回libevent被編譯以支持的方法列表。然而libevent運行的時候,操作系統可能不能支持所有方法。比如說,可能OS X版本中的kqueue的bug太多,無法使用。
3.2 接口
const char *event_base_get_method(const struct event_base *base);
enum event_method_feature event_base_get_features(const struct event_base *base);event_base_get_method()返回event_base正在使用的方法。event_base_get_features()返回event_base支持的特征的比特掩碼。
示例
struct event_base *base;
enum event_method_feature f;
base = event_base_new();
if (!base) {
puts("Couldn't get an event_base!");
} else {
printf("Using Libevent with backend method %s.",
event_base_get_method(base));
f = event_base_get_features(base);
if ((f & EV_FEATURE_ET))
printf(" Edge-triggered events are supported.");
if ((f & EV_FEATURE_O1))
printf(" O(1) event notification is supported.");
if ((f & EV_FEATURE_FDS))
printf(" All FD types are supported.");
puts("");
}這個函數定義在<event2/event.h>中。event_base_get_method()首次出現在1.4.3版本中,其他函數首次出現在2.0.1-alpha版本中。
4 釋放event_base
使用完event_base之后,使用event_base_free()進行釋放。
接口
void event_base_free(struct event_base *base);注意:這個函數不會釋放當前與event_base關聯的任何事件,或者關閉他們的套接字,或者釋放任何指針。
event_base_free()定義在<event2/event.h>中,首次由libevent 1.2實現。
5 設置event_base的優先級
libevent支持為事件設置多個優先級。然而,event_base默認只支持單個優先級??梢哉{用event_base_priority_init()設置event_base的優先級數目。
接口
int event_base_priority_init(struct event_base *base, int n_priorities);
成功時這個函數返回0,失敗時返回-1。base是要修改的event_base,n_priorities是要支持的優先級數目,這個數目至少是1。每個新的事件可用的優先級將從0(最高)到n_priorities-1(最低)。
常量EVENT_MAX_PRIORITIES表示n_priorities的上限。調用這個函數時為n_priorities給出更大的值是錯誤的。
注意
必須在任何事件激活之前調用這個函數,最好在創建event_base后立刻調用。
示例
關于示例,請看event_priority_set的文檔。
默認情況下,與event_base相關聯的事件將被初始化為具有優先級n_priorities / 2。event_base_priority_init()函數定義在<event2/event.h>中,從libevent 1.0版就可用了。
6 在fork()之后重新初始化event_base
不是所有事件后端都在調用fork()之后可以正確工作。所以,如果在使用fork()或者其他相關系統調用啟動新進程之后,希望在新進程中繼續使用event_base,就需要進行重新初始化。
接口
int event_reinit(struct event_base *base);成功時這個函數返回0,失敗時返回-1。
示例
struct event_base *base = event_base_new();
/*
add some events to the event_base
*/
if (fork()) {
/* In parent */
continue_running_parent(base); /*
*/
} else {
/* In child */
event_reinit(base);
continue_running_child(base); /*
*/
}event_reinit()定義在<event2/event.h>中,在libevent 1.4.3-alpha版中首次可用。
7 廢棄的event_base函數
老版本的libevent嚴重依賴“當前”event_base的概念。“當前”event_base是一個由所有線程共享的全局設置。如果忘記指定要使用哪個event_base,則得到的是當前的。因為event_base不是線程安全的,這很容易導致錯誤。
老版本的libevent沒有event_base_new(),而有:
接口
struct event_base *event_init(void);這個函數的工作與event_base_new()類似,它將分配的event_base設置成當前的。沒有其他方法改變當前event_base。
本文描述的函數有一些用于操作當前event_base的變體,這些函數與新版本函數的行為類似,只是它們沒有event_base參數。
這個函數當前僅在Windows上使用IOCP時有用,雖然將來可能在其他平臺上有用。這個函數告訴event_config在生成多線程event_base的時候,應該試圖使用給定數目的CPU。注意這僅僅是一個提示:event_base使用的CPU可能比你選擇的要少。
示例
大多數程序使用這個函數就夠了。
event_base_new()函數聲明在<event2/event.h>中,首次出現在libevent 1.4.3版。
2 創建復雜的event_base
要對取得什么類型的event_base有更多的控制,就需要使用event_config。event_config是一個容納event_base配置信息的不透明結構體。需要event_base時,將event_config傳遞給event_base_new_with_config()。
2.1 接口
struct event_base *event_base_new_with_config(const struct event_config *cfg);
void event_config_free(struct event_config *cfg);
要使用這些函數分配event_base,先調用event_config_new()分配一個event_config。然后,對event_config調用其它函數,設置所需要的event_base特征。最后,調用event_base_new_with_config()獲取新的event_base。完成工作后,使用event_config_free()釋放event_config。
2.2 接口
enum event_method_feature {
EV_FEATURE_ET = 0x01,
EV_FEATURE_O1 = 0x02,
EV_FEATURE_FDS = 0x04,
};
int event_config_require_features(struct event_config *cfg,
enum event_method_feature feature);
enum event_base_config_flag {
EVENT_BASE_FLAG_NOLOCK = 0x01,
EVENT_BASE_FLAG_IGNORE_ENV = 0x02,
EVENT_BASE_FLAG_STARTUP_IOCP = 0x04,
EVENT_BASE_FLAG_NO_CACHE_TIME = 0x08,
EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST = 0x10,
EVENT_BASE_FLAG_PRECISE_TIMER = 0x20
};
int event_config_set_flag(struct event_config *cfg,
enum event_base_config_flag flag);
調用event_config_avoid_method()可以通過名字讓libevent避免使用特定的可用后端。調用event_config_require_feature()讓libevent不使用不能提供所有指定特征的后端。調用event_config_set_flag()讓libevent在創建event_base時設置一個或者多個將在下面介紹的運行時標志。
event_config_require_features()可識別的特征值有:
v EV_FEATURE_ET:要求支持邊沿觸發的后端
v EV_FEATURE_O1:要求添加、刪除單個事件,或者確定哪個事件激活的操作是O(1)復雜度的后端
v EV_FEATURE_FDS:要求支持任意文件描述符,而不僅僅是套接字的后端
event_config_set_flag()可識別的選項值有:
v EVENT_BASE_FLAG_NOLOCK:不要為event_base分配鎖。設置這個選項可以為event_base節省一點用于鎖定和解鎖的時間,但是讓在多個線程中訪問event_base成為不安全的。
v EVENT_BASE_FLAG_IGNORE_ENV:選擇使用的后端時,不要檢測EVENT_*環境變量。使用這個標志需要三思:這會讓用戶更難調試你的程序與libevent的交互。
v EVENT_BASE_FLAG_STARTUP_IOCP:僅用于Windows,讓libevent在啟動時就啟用任何必需的IOCP分發邏輯,而不是按需啟用。
v EVENT_BASE_FLAG_NO_CACHE_TIME:不是在事件循環每次準備執行超時回調時檢測當前時間,而是在每次超時回調后進行檢測。注意:這會消耗更多的CPU時間。
v EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST:告訴libevent,如果決定使用epoll后端,可以安全地使用更快的基于changelist的后端。epoll-changelist后端可以在后端的分發函數調用之間,同樣的fd多次修改其狀態的情況下,避免不必要的系統調用。但是如果傳遞任何使用dup()或者其變體克隆的fd給libevent,epoll-changelist后端會觸發一個內核bug,導致不正確的結果。在不使用epoll后端的情況下,這個標志是沒有效果的。也可以通過設置EVENT_EPOLL_USE_CHANGELIST環境變量來打開epoll-changelist選項。
上述操作event_config的函數都在成功時返回0,失敗時返回-1。
注意
設置event_config,請求OS不能提供的后端是很容易的。比如說,對于libevent 2.0.1-alpha,在Windows中是沒有O(1)后端的;在Linux中也沒有同時提供EV_FEATURE_FDS和EV_FEATURE_O1特征的后端。如果創建了libevent不能滿足的配置,event_base_new_with_config()會返回NULL。
2.3 接口
這個函數當前僅在Windows上使用IOCP時有用,雖然將來可能在其他平臺上有用。這個函數告訴event_config在生成多線程event_base的時候,應該試圖使用給定數目的CPU。注意這僅僅是一個提示:event_base使用的CPU可能比你選擇的要少。
示例
struct event_base *base;
int i;
/* My program wants to use edge-triggered events if at all possible. So
I'll try to get a base twice: Once insisting on edge-triggered IO, and
once not. */
for (i=0; i<2; ++i) {
cfg = event_config_new();
/* I don't like select. */
event_config_avoid_method(cfg, "select");
if (i == 0)
event_config_require_features(cfg, EV_FEATURE_ET);
base = event_base_new_with_config(cfg);
event_config_free(cfg);
if (base)
break;
/* If we get here, event_base_new_with_config() returned NULL. If
this is the first time around the loop, we'll try again without
setting EV_FEATURE_ET. If this is the second time around the
loop, we'll give up. */
}
這些函數和類型在<event2/event.h>中聲明。
EVENT_BASE_FLAG_IGNORE_ENV標志首次出現在2.0.2-alpha版本。event_config_set_num_cpus_hint()函數是2.0.7-rc版本新引入的。本節的其他內容首次出現在2.0.1-alpha版本。
3 檢查event_base的后端方法
有時候需要檢查event_base支持哪些特征,或者當前使用哪種方法。
3.1 接口
event_get_supported_methods()函數返回一個指針,指向libevent支持的方法名字數組。這個數組的最后一個元素是NULL。
示例
const char **methods = event_get_supported_methods();
printf("Starting Libevent %s. Available methods are:\n",
event_get_version());
for (i=0; methods[i] != NULL; ++i) {
printf(" %s\n", methods[i]);
}
注意
這個函數返回libevent被編譯以支持的方法列表。然而libevent運行的時候,操作系統可能不能支持所有方法。比如說,可能OS X版本中的kqueue的bug太多,無法使用。
3.2 接口
enum event_method_feature event_base_get_features(const struct event_base *base);
event_base_get_method()返回event_base正在使用的方法。event_base_get_features()返回event_base支持的特征的比特掩碼。
示例
enum event_method_feature f;
base = event_base_new();
if (!base) {
puts("Couldn't get an event_base!");
} else {
printf("Using Libevent with backend method %s.",
event_base_get_method(base));
f = event_base_get_features(base);
if ((f & EV_FEATURE_ET))
printf(" Edge-triggered events are supported.");
if ((f & EV_FEATURE_O1))
printf(" O(1) event notification is supported.");
if ((f & EV_FEATURE_FDS))
printf(" All FD types are supported.");
puts("");
}
這個函數定義在<event2/event.h>中。event_base_get_method()首次出現在1.4.3版本中,其他函數首次出現在2.0.1-alpha版本中。
4 釋放event_base
使用完event_base之后,使用event_base_free()進行釋放。
接口
注意:這個函數不會釋放當前與event_base關聯的任何事件,或者關閉他們的套接字,或者釋放任何指針。
event_base_free()定義在<event2/event.h>中,首次由libevent 1.2實現。
5 設置event_base的優先級
libevent支持為事件設置多個優先級。然而,event_base默認只支持單個優先級??梢哉{用event_base_priority_init()設置event_base的優先級數目。
接口
成功時這個函數返回0,失敗時返回-1。base是要修改的event_base,n_priorities是要支持的優先級數目,這個數目至少是1。每個新的事件可用的優先級將從0(最高)到n_priorities-1(最低)。
常量EVENT_MAX_PRIORITIES表示n_priorities的上限。調用這個函數時為n_priorities給出更大的值是錯誤的。
注意
必須在任何事件激活之前調用這個函數,最好在創建event_base后立刻調用。
示例
關于示例,請看event_priority_set的文檔。
默認情況下,與event_base相關聯的事件將被初始化為具有優先級n_priorities / 2。event_base_priority_init()函數定義在<event2/event.h>中,從libevent 1.0版就可用了。
6 在fork()之后重新初始化event_base
不是所有事件后端都在調用fork()之后可以正確工作。所以,如果在使用fork()或者其他相關系統調用啟動新進程之后,希望在新進程中繼續使用event_base,就需要進行重新初始化。
接口
成功時這個函數返回0,失敗時返回-1。
示例
/*


if (fork()) {
/* In parent */
continue_running_parent(base); /*

} else {
/* In child */
event_reinit(base);
continue_running_child(base); /*

}
event_reinit()定義在<event2/event.h>中,在libevent 1.4.3-alpha版中首次可用。
7 廢棄的event_base函數
老版本的libevent嚴重依賴“當前”event_base的概念。“當前”event_base是一個由所有線程共享的全局設置。如果忘記指定要使用哪個event_base,則得到的是當前的。因為event_base不是線程安全的,這很容易導致錯誤。
老版本的libevent沒有event_base_new(),而有:
接口
這個函數的工作與event_base_new()類似,它將分配的event_base設置成當前的。沒有其他方法改變當前event_base。
本文描述的函數有一些用于操作當前event_base的變體,這些函數與新版本函數的行為類似,只是它們沒有event_base參數。