請注意這是 libev 而不是 libevent 的文章!html
自從接觸到 libev 以後,就深深贊同做者精簡的設計理念,因而就愛上了 libev 這樣簡單的I/O庫。此外,libev 的大小也比 libevent 小得多而且自由得多。雖然我在公司的項目用的異步 I/O 庫仍是以 libevent 和 libubox 爲主,可是我的業餘的工程中,每每用的是 libev 而不是 libevent。編程
惋惜的是,貌似是由於 libev 是單人維護,並且不支持 Windows 等緣由,並不如 libevent 甚至是 libuv 等受歡迎,國內的研究資料也並很少。segmentfault
可是呢,老子是 Linux / BSD 開發者,我就喜歡!後端
閱讀本文最好有 libevent 基礎,由於基本概念和 libevent 是相似的安全
庫 事件循環 具體事件 ---------------------------------- libevent event_loop event libev ev_loop watcher
其餘的在這裏我就不重複了。服務器
關於 libevent 請參見個人文章:Libevent官方文檔學習筆記網絡
本文地址:http://www.javashuo.com/article/p-qlmywexr-dw.html異步
ev_io
:支持 Linux 的select
、poll
、epoll
;BSD 的kqueue
;Solaris 的event port mechanisms
ev_signal
:支持各類信號處理、同步信號處理ev_timer
:相對事件處理ev_periodic
:排程時間表ev_child
:進程狀態變化事件ev_start
:監視文件狀態ev_fork
:有限的fork事件支持Libev 使用一個ev_tstamp
數據類型來表示1970年以來的秒數,實際類型是 C 裏面的double
類型。函數
Libev 使用三種層級的錯誤:oop
ev_set_syserr_cb
所設置的回調。默認行爲是調用abort()
assert
assert
如下函數能夠在任意時間調用,用於配置 libev 庫:
ev_tstamp ev_time ();
返回當前的時間。
void ev_sleep (ev_tstamp interval);
休眠一段指定的時間。若是interval
小於等於0,則馬上返回。最大支持一天,也就是86400秒
int ev_version_major (); int ev_version_minor ();
能夠調用這兩個函數,而且與系統與定義的EV_VERSION_MAJOR
和EV_VERSION_MINOR
做對比,判斷是否應該支持該庫
unsigned int ev_supported_backends (); unsigned int ev_recommand_backends (); unsigned int ev_embeddable_backends ();
返回該 libev 庫支持的和建議的後端列表
void ev_set_allocator ( void *(*cb)(void *ptr, long size)throw() );
從新設置realloc
函數。對於一些系統(至少包括 BSD 和 Darwin)的 realloc 函數可能不正確,libev 已經給了替代方案。
void ev_set_syserr_cb ( void (*cb)(const char *msg)throw() );
設置系統錯誤的 callback。默認調用perror()
並abort()
void ev_feed_signal (int signum)
模擬一個signal
事件出來
Event loop 用一個結構體struct ev_loop *
描述。Libev 支持兩類 loop,一是 default loop,支持 child process event;動態建立的 event loops 就不支持這個功能
struct ev_loop *ev_default_loop (unsigned int flags);
初始化 default loops。若是已經初始化了,那麼直接返回而且忽略 flags。注意這個函數並非線程安全的。只有這個 loop 能夠處理ev_child
事件。
struct ev_loop *ev_loop_new (unsigned int flags);
這個函數是線程安全的。通常而言,每一個 thread 使用一個 loop。如下說明 flag 項的各個值:
EVFLAG_AUTO
:默認值,經常使用EVFLAG_NOENV
:指定 libev 不使用LIBEV_FLAGS
環境變量。經常使用於調試和測試EVFLAG_FORKCHECK
:與ev_loop_fork()
相關,本文暫略EVFLAG_NOINOTIFY
:在ev_stat
監聽中不使用inotify
APIEVFLAG_SIGNALFD
:在ev_signal
監聽中使用signalfd
APIEVFLAG_NOSIGMASK
:使 libev 避免修改 signal mask。這樣的話,你要使 signal 是非阻塞的。在將來的 libev 中,這個 mask 將會是默認值。EVBACKEND_SELECT
:通用後端EVBACKEND_POLL
:除了 Windows 以外的全部後端均可以用EVBACKEND_EPOLL
:Linux 後端EVBACKEND_KQUEUE
:大多數 BSD 的後端EVBACKEND_DEVPOLL
:Solaris 8 後端EVBACKEND_PORT
:Solaris 10 後端void ev_loop_destroy (struct ev_loop *loop);
銷燬ev_loop
。注意這裏要將全部的 IO 清除光以後再調用,由於這個函數並不停止全部活躍(active)的 IO。部分 IO 不會被清除,好比 signal。這些須要手動清除。這個函數通常和ev_loop_new
一塊兒出如今同一個線程中。
void ev_loop_fork (struct ev_loop *loop);
這個函數致使ev_run
的子過程重設已有的 backend 的 kernel state。重用父進程建立的 loop。能夠和pthread_atfork()
配合使用。
須要在每個須要在 fork 以後重用的 loop 中調用這個函數。必須在恢復以前或者調用ev_run()
以前調用。若是是在fork
以後建立的 loop,不須要調用。
使用 pthread 的代碼例以下:
static void post_fork_chuild (void) { ev_loop_fork (EV_DEFAULT); } ... pthread_atfork (NULL, NULL, post_fork_child);
int ev_is_default_loop (struct ev_loop *loop);
判斷當前 loop 是否是 default loop。
unsigned int ev_iteration (struct ev_loop *loop);
返回當前的 loop 的迭代數。等於 libev pool 新事件的數量(?)。這個值對應ev_prepare
和ev_check
調用,並在 prepare 和 check 之間增一。
unsigned int ev_depth (struct ev_loop *loop);
返回ev_run()
進入減去退出次數的差值。
注意,致使ev_run
異常退出的調用(setjmp / longjmp, pthread_cancel, 拋出異常等)均不會致使該值減一。
unsigned int ev_backend (struct ev_loop *loop);
返回EVBACKEND_*
值
ev_tstamp ev_now (loop)
獲得當前的「event loop time」。在 callback 調用期間,這個值是不變的。
void ev_new_update (loop)
更新從ev_now()
中返回的時間。沒必要要的話,不要使用,由於這個函數的開銷相對是比較大的。
void ev_suspend (struct ev_loop *loop); void ev_resume (struct ev_loop *loop);
暫停當前的 loop,使其颳起當前的全部工做。同時其 timeout 也會暫停。若是恢復後,timer 會從上一次暫停狀態繼續及時——這一點對於實現一些要連同時間也一塊兒凍結的功能時,很是有用。
注意已經 resume 的loop不能再 resume,反之已經 suspend 的 loop 不能再 suspend。
bool ev_run (struct ev_loop *loop, int flags);
初始化 loop 結束後,調用這個函數開始 loop。若是 flags == 0,直至 loop 沒有活躍的時間或者是調用了 ev_bread 以後中止。
Loop 能夠是異常使能的,你能夠在 callback 中調用longjmp
來終端回調而且跳出 ev_run,或者經過拋出 C++ 異常。這些不會致使 ev_depth 值減小。
EVRUN_NOWAIT
會檢查而且執行全部未解決的 events,但若是沒有就緒的時間,ev_run 會馬上返回。EVRUN_ONCE
會檢查全部的 events,在至少每個 event 都執行了一次事件迭代以後才返回。但有時候,使用ev_prepare
/ev_check
更好。
如下是ev_run
的大體工做流程:
ev_break
狀態LOOP:
EVFLAG_FORKCHECK
,則檢查 fork,若是檢測到 fork,則排隊並調用全部的 fork watchersev_break
被調用了,則直接跳轉至 FINISHev_now
的值ev_now
的值,執行 time jump 調整ev_break
被調用了,或者使用了EVRUN_ONCE
或者EVRUN_NOWAIT
,則若是沒有活躍的 watchers,則 FINISH,不然 continueFINISH:
EVBREAK_ONE
,則重設 ev_break 狀態void ev_break (struct ev_loop *loop, how);
中斷 loop。參數能夠是 EVBREAK_ONE
(執行完一個內部調用後返回)或EVBREAK_ALL
(執行完全部)。
下一次調用 ev_run 的時候,相應的標誌會清除
void ev_ref (struct ev_loop *loop); void ev_unref (struct ev_loop *loop);
相似於 Objective-C 中的引用計數,只要 reference count 不爲0,ev_run 函數就不會返回。
在作 start 以後要 unref;stop 以前要 ref。
void ev_set_io_collect_interval (struct ev_loop *loop, ev_tstamp interval); void ev_set_timeout_collect_interval (struct ev_loop *loop, ev_tstamp interval);
兩個值均默認爲0,表示儘可能以最小的延遲調用 callback。但這是理想的狀況,實際上,好比 select 這樣低效的系統調用,因爲能夠一次性讀取不少,因此能夠適當地進行延時。經過使用比較高的延遲,可是增長每次處理的數據量,以提升 CPU 效率。
void ev_invoke_pending (struct ev_loop *loop);
調用全部的 pending 的 watchers。這個除了能夠在 callback 中調用(少見)以外,更多的是在重載的函數中使用。參見下一個函數
void ev_set_invoke_pending_cb (struct ev_loop *loop, void (*invoke_pending_cb(EV_P)));
重載 ev_loop 調用 watchers 的函數。新的回調應調用 ev_invoke_pending
。若是要恢復默認值,則置喙 ev_invoke_pending 便可。
int ev_pending_count (struct ev_loop *loop);
返回當前有多少個 pending 的 watchers。
void ev_set_loop_release_cb (struct ev_loop *loop, void (*release)(EV_P)throw(), void (*acquire)(EV_P)throw());
這是一個 lock 操做,你能夠自定義 lock。其中 release 是 unlock,acquire 是 lock。release 是在 loop 掛起以等待events 以前調用,而且在開始回調以前調用 acquire。
void ev_set_userdata (struct ev_loop *loop, void *data); void *ev_userdata (struct ev_loop *loop);
設置 / 讀取 loop 中的用戶 data。這一點和 libevent 很不一樣,libevent 的參數 / 用戶數據是以 event 爲單位的,而 libev 的原生用戶數據是以 loop 爲單位的。
void ev_verify (struct ev_loop *loop);
驗證當前 loop 的設置。若是發現問題,則打印 error msg 並 abort()
。
Libev 官方文檔學習筆記(1)——概述和 ev_loop(本文)
Libev 官方文檔學習筆記(2)——watcher 基礎
Libev 官方文檔學習筆記(3)——經常使用 watcher 接口
使用 libev 構建 TCP 響應服務器的簡單流程