https://www.cnblogs.com/wunaozai/p/3950249.html Libev庫學習(1)
https://www.cnblogs.com/wunaozai/p/3954131.html Libev庫學習(2)
https://www.cnblogs.com/wunaozai/p/3955156.html Libev庫學習(3)
https://www.cnblogs.com/wunaozai/p/3960494.html Zlib庫的安裝與使用html
https://segmentfault.com/a/1190000006173864 01:概述和 ev_loop
https://segmentfault.com/a/1190000006200077 02:watcher 基礎
https://segmentfault.com/a/1190000006679929 03:經常使用 watcher 接口java
https://www.cnblogs.com/gqtcgq/p/7247102.html Libev中的相對時間定時器 ev_timer
https://www.cnblogs.com/gqtcgq/p/7247100.html Libev中的絕對時間定時器 ev_periodic
https://www.cnblogs.com/gqtcgq/p/7247095.html Libev源碼分析08:Libev中的信號監視器nginx
http://blog.chinaunix.net/uid-25203957-id-1753940.html Libev 初步
http://blog.chinaunix.net/uid-25203957-id-1760908.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
類型。segmentfault
Libev 使用三種層級的錯誤:後端
ev_set_syserr_cb
所設置的回調。默認行爲是調用abort()
assert
assert
如下函數能夠在任意時間調用,用於配置 libev 庫:安全
ev_tstamp ev_time ();
返回當前的時間。併發
void ev_sleep (ev_tstamp interval);
休眠一段指定的時間。若是interval
小於等於0,則馬上返回。最大支持一天,也就是86400秒app
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()
。
如下是一段示意性的代碼,使用的是ev_io
:
static void my_cb (struct ev_loop *loop, ev_io *w, int revents)
{
ev_io_stop (w);
ev_break (loop, EVBREAK_ALL);
}
some_main()
{
...
struct ev_loop *loop = ev_default_loop (0);
ev_io stdin_watcher;
ev_init (&stdin_watcher, my_cb);
ev_io_set (&stdin_watcher, STDIN_FILENO, EV_READ);
ev_io_start (loop, &stdin_watcher);
ev_run (loop, 0);
...
}
每個 watcher 類型有一個附屬的 watcher 結構體。(通常是struct ev_XXX
或ev_XXX
)
每個 watcher 結構都須要用ev_init
初始化,每個 watcher 都有對應的ev_XXX_set
函數、ev_XXX_start
函數、ev_XXX_stop
函數。在 ev_run 以前進行各個 watcher 的 ev_start。
只要 watcher 是 active,就不能再調用 init。
每一個 callback 都有三個參數:loop, watcher, 事件的掩碼值。可能的掩碼值有:
EV_READ
EV_WRITE
EV_TIMER
:ev_timer 超時EV_PERIODIC
:ev_periodic 超時EV_SIGNAL
:某線程接收了 ev_signal 中指定的 signalEV_CHILD
:ev_child 中指定的 pid 得到了一個狀態變化EV_STAT
:ev_stat 中指定的 path 的屬性修改了EV_IDLE
:ev_idle watcher 發現無事可作EV_PREPARE
, EV_CHECK
:全部 ev_prepare watchers 在 loop 開始收集事件前調用;全部ev_check watchers 則在之後調用。回調可在這兩個 watchers 中開始/中止相應的 watchers。EV_EMBED
:ev_embed watcherEV_CLEANUP
:event loop 即將被銷燬EV_ASYNC
:asuny watcher 已經被異步通知EV_CUSTOM
:不是 libev 發送的信號。參見ev_feed_event
EV_ERROR
:在 libev 內存不夠用時可能產生;fd 被外部關閉時也可能產生void ev_init (ev_TYPE *watcher, callback)
使用這個宏初始化 watcher。此外還須要調用相應的 ev_XXX_set
函數。參見下文:
void ev_TYPE_set (ev_TYPE *watcher, [args])
設置指定類型的 wetaher。init 函數必須在此以前被調用一次,此後能夠設置任意次的 set 函數。
不能對一個 active 的 watcher 調用此函數,但 pending 能夠。好比:ev_io w;
ev_init (&w, my_cb);
ev_io_set (&w, STDIN_FILENO, EV_READ);
void ev_TYPE_set (ev_TYPE *watcher, callback, [args])
這個宏將 init 和 set 糅合在一塊兒使用
void ev_TYPE_start (loop, ev_TYPE *watcher)
開始(激活)指定的 watcher。若是 watcher 已是 active,則調用無效。
void ev_TYPE_stop (loop, ev_TYPE *watcher)
中止 watcher,並清空 pending 狀態。若是要釋放一個 Watcher,最好都顯式地調用 stop。
bool ev_is_active (ev_TYPE *watcher)
若是 watcher 被執行了一次 start,而且未被 stop,則返回 true。
bool ev_is_pending (ev_TYPE *watcher)
當且僅當 watcher pending 時返回 true。(如:有未決的事件,可是 callback 未被調用)
callback ev_cb (ev_TYPE *watcher)
void ev_set_cb (ev_TYPE *watcher, callback)
讀 / 寫 callback
void ev_set_priority (ev_TYPE *watcher, int priority)
int ev_priority (ev_TYPE *watcher)
Priority 是一個介於EV_MAXPRI
(默認2)和EV_MIN_PRI
(默認-2)之間的值。數值越高越優先被調用。但除了 ev_idle,每個 watcher 都會被調用。
當 watcher 是 active 或 pending 時並不能修改。
實際上 priority 大於-2到2的範圍也是沒問題的。
void ev_invoke (loop, ev_TYPE *watcher, int revents);
使用指定的參數調用 callback
int ev_clear_pending (loop, ev_TYPE *watcher);
清除指定 watcher 的 pending 狀態,而且返回 revents 位。若是 watcher 不是 pending 則返回0
void ev_feed_event (loop, ev_TYPE *watcher, int revents)
模擬一個事件。參見ev_feed_fd_event
和ev_feed_signal_event
除了前文說起的 active 和 pending 狀態以外,本小節描述了更加詳細的 watcher 狀態。
initialized
:經過調用ev_TYPE_init
對 watcher 進行初始化,這是註冊到 loop 以前的必要步驟。能夠再次調用 ev_TYPE_init 進行操做。
started
/running
/active
:調用ev_TYPE_start
以後的狀態,而且開始等待事件。在這個狀態下,除了特別說起的少數狀況以外,它不能存取、移動、釋放,只能維持着對它的指針。
pending
:當 watcher 是 active 而且一個讓 watcher 感興趣的事件到來,那麼 watcher 進入 pending。這個狀態的 watcher 能夠 access,但不能存取、移動、釋放。
stopped
:調用ev_TYPE_stop
,此時狀態與 initialized 相同。
這個 watcher 負責檢測文件描述符(如下簡稱fd)是否可寫入數據或者是讀出數據。最好是將fd設置爲非阻塞的。
注意有時候在調用read
時是沒有數據的(返回0),此時一個一個非阻塞的read
會獲得EAGAIN
錯誤。
(如下兩個特殊問題,是 libev 文檔中特別提到的,可是我看不太懂……)
部分系統須要顯式地調用close
(如kqueue
、epoll
),不然當一個 fd 消失、而新的 fd 進入,佔用同一個 fd 號時,libev
不知道這是一個新的fd。
libev 一側解決的辦法是每次調用ev_io_set
時,都假定這是一個新的 fd。
dup
操做 fd 的特殊問題一些後端(backend)不能註冊普通的 fd 事件,只能註冊underlying file descriptions
,這意味着使用dup()
或其餘奇怪操做的fd,只能由其中一個被接收到。
這沒有有效的解決辦法,除非將後端設置爲BACKEND_SELECT
或EVBACKEND_POLL
ev_io
對於文件淚說沒有什麼用,只要文件存在,就當即會有時間。對於stdin
和stdout
,請謹慎使用,確保這二者沒有被重定向至文件。
記得使用ev_loop_fork
,而且使用EVFLAG_FORKCHECK
。不過對於epoll
和kqueue
以外的無需擔憂。
只是提醒一下:記得處理SIGPIPE
事件。
accept
一個沒法接受的鏈接大多數 POSIX accpet 實現中在刪除由於錯誤而致使的鏈接時(如 fd 到達上限)都回產生一個錯誤的操做,好比使 accept 失敗但不拒絕鏈接,只產生ENFILE
錯誤。但這個會致使 libev 仍是將其標記爲 ready 狀態。
推薦方法是列出全部的錯誤並記錄下來,或者是暫時關閉 watchers。
void ev_io_init (ev_io *, callback, int fd, int events)
void ev_io_set (ev)io *, int fd, int events)
其中 events 能夠是EV_WRITE
和EV_READ
的組合。
static void stdin_readable_db (struct ev_loop *loop,
ev_io *w,
int revents)
{
ev_io_stop (loop, w)
...... // 從 w->fd 中進行read
}
......
some_init_func ()
{
......
struct ev_loop *loop = ev_default_init (0);
ev_io stdin_readable;
ev_io_init (&stdin_readable, stdin_readable_db , STDIN_FILENO, EV_READ);
ev_io_start (loop, &stdin_readable);
ev_run (loop, 0);
...
}
Libev 提供了一個相對超時機制的定時器。所謂的「相對」,就是說這個定時器的參數是:指定以當前時間爲基準,延遲多久出發事件。這個定時器與基於萬年曆的日期/時間是無關的,只基於系統單調時間。
下面列出一個以60秒爲單位的循環定時器做爲例子,來講明使用ev_timer的不一樣策略
ev_timer_init (timer, callback, 60.0, 6.0);
ev_timer_start (loop, timer)
標準設置。或——
ev_timer_stop (loop, timer);
ev_timer_set (timer, 60.0, 0.0);
ev_timer_start (loop, timer)
這樣的設置,當每次有活躍時間時,中止timer,而且重啓它。第一個參數是首次超時,第二個參數是第二次開始的固定超時時間。
可是這樣的方法雖然比較簡易,可是時間不穩定,並且開銷較大
ev_timer_again
重設使用ev_timer_again
,能夠忽略ev_timer_start
ev_init (timer, callback);
timer->repeat = 60.0;
ev_timer_again (loop, start);
上面的初始化完成後,在 callback 裏調用:
timer->repeat = 60.0;
ev_timer_again (loop, timer);
能夠改變 timeout 值,無論 timer 是否 active
這個方式的基本思路是由於許多 timeout 時間都比 interval 大不少,此時要記住上一次活躍的時間,而後再 callback 中檢查真正的 timeout
ev_tstamp g_timeout = 60.0;
ev_tstamp g_last_activity;
ev_timer g_timer;
static void callback (EV_P_ev_timer *w, int revents) {
ev_tstamp after = g_last_activity - ev_now(EV_A) + g_timeout;
// 若是小於零,表示時間已經發生了,已超時
if (after < 0.0) {
...... // 執行 timeout 操做
}
else {
// callback 被調用了,可是卻有一些最近的活躍操做,說明未超時
// 此時就按照須要設置的新超時事件來處理
ev_timer_set (w, after, 0.0);
ev_timer_start (loop, g_timer);
}
}
啓用這種模式,記得初始化時將g_last_activity
設置爲ev_now
,而且調用一次callback (loop, &g_timer, 0)
;當活躍時間到來時,只需修改全局的 timeout 變量便可,而後再調用一次 callback
g_timeout = new_value
ev_timer_stop (loop, &timer)
callback (loop, &g_timer, 0)
使用場景:有成千上萬個請求,而且都須要 timeouts
當 timeout 開始前,計算 timeout 的值,而且將 timeout 放在鏈表末尾。而後當鏈表前面的項須要 fire 時。使用ev_timer
來將其 fire 掉。
當有 activity 時,將 timer 從 list 中一處,重算 timeout,而且再附到 list 末尾,確保若是ev_timer
已經被 list 的第一項取出時,更新它
假設在500.9秒的時候請求延時1秒,那麼當501秒到來時,可能致使 timeout,這就是「太早」問題。Libev的策略是對於這種狀況,在502秒時才執行 timeout。可是這又有「太晚」的問題,請程序員注意.
Suspenged animation,也稱爲休眠,指的是將機子置於休眠狀態。注意不一樣的機子不一樣的系統這個行爲可能不同。
其中一種休眠是使得全部程序感受只是通過了很小的一段時間通常(時間跳躍)
推薦在SIGTSTP
處理中調用ev_suspend
和ev_resume
ev_now_update()
的開銷很大,請謹慎使用
Libev使用的時一個內部的單調時鐘而不是系統時鐘,而ev_timer
則是基於系統時鐘的,因此在作比較的時候二者不一樣步。
void ev_timer_init (ev_timer *, callback, ev_tstamp after, ev_tstamp repeat);
void ev_timer_set (ev_timer *, ev_tstamp after, ev_tstamp repeat);
若是repeat爲正,這個timer會重複觸發,不然只觸發一次。
void ev_timer_again (loop, ev_timer *)
ev_tstamp ev_timer_remaining (loop, ev_timer *)
void ev_periodic_init (ev_periodic *, callback, ev_tstamp offset,
ev_tstamp interval, reschedule_cb)
void ev_periodic_set (ev_periodic *, ev_tstamp offset,
ev_tstamp interval, reschedule_cb)
如下是幾種不一樣應用場景的設置方法:
ev_tstamp (*reschedule_cb)(ev_periodic *w, ev_tstamp now);
例程是:
static ev_tstamp my_scheduler (...) {
return now + 60.0;
}
相似於 Linux 內核的jiffies
,返回下一個時間點。
這個timer很是便於用來提供諸如「下一個正午12點」之類的定時器。
void ev_periodic_again (loop, ev_periodic *)
關閉並重啓 watcher,參見前文。
ev_tstamp ev_periodic_at (ev_periodic *)
返回下一次觸發的絕對時間。
在哦你跟一個 loop 能夠屢次觀測同一個 signal,可是沒法在多個 loop 中觀測同一個 signal。此外,SIGCHILD
只能在 default loop 中監聽。
在子進程調用 exec
以前,應當將 signal mask 重設爲你所需的默認值。最簡單的方法就是子進程作一個pthread_atfork()
來重設。
POSIX 的很多功能(如sigwait)只有在進程中的全部線程屏蔽了 signal 時才真正生效
爲了解決這個問題,若是真的要使用這些功能的話,建議在建立線程以前屏蔽全部的 signal,而且在建立 loops 的時候指定EVFLAG_NOSIGMASK
,而後制定一個 thread 用來接收 signals。
void _ev_signal_init (ev_signal *, callback, int signum)
void ev_signal_set (ev_signal *, int signum)
當接收到SIGCHILD
事件時,child watcher 觸發。大部分狀況下,子進程退出或被殺掉。只要這個 watcher 的 loop 未開始,你甚至能夠在 shild 被 fork 以後才加入 child watcher。
Ev_child 的優先級固定是EV_MAXPRI
。
void ev_chile_init (ev_child *, callback, int pid, int trace)
void ev_child_set (ev_child *, int pid, int trace)
Pid 若是指定0的話,表示任意子進程。能夠在 ev_child 中觀察rstatus
成員來了解子進程狀態。
int pid;
表示監控中的 pid,只讀。
int rpid;
可讀寫,表示檢測到狀態變化的 pid
int tstatus;
可讀寫,表示由 rpid 致使的進程的 exit/trace 狀態值。
使用 ev_stat 時,監控目標位置上無需存在文件,由於文件從「不存在」變爲存在也是一種狀態變化。
文件路徑必須是絕對路徑,不能存在「./
」或「../
」。
Ev_stat 的實現其實只是按期調用stat()
來判斷文件屬性的變化,因此能夠指定檢查週期。指定0的話會使用默認事件週期。
正由於這是輪詢操做,因此這個功能不適合作大數據量或者是大併發檢測;同時,ev_stat 是異步的。
默認關閉大文件支持(使用32位的stat
)。若是要使用大文件支持(ABI),libev 的做者在這裏吐槽,說你要遊說操做系統的發佈方去支持……囧rz
有些系統的文件時間僅精確到秒,這就意味着 ev_stat 沒法區分秒如下的變更。
void ev_stat_init (ev_stat *, callback, const char *path, ev_tstamp interval);
void ev_stat_set (ev_stat *, const char *path, ev_tstamp interval);
void ev_stat_stat (loop, ev_stat *);
第三個函數使用新的文件 stat 值去更新 stat buffer,使用此函數來使得你作的一些配置更改不會被觸發。
ev_statdata attr
只讀,表明文件最近一次的狀態。ev_statdata
和struct stat
基本是相通的。
ev_statdata prev
文件上一次的狀態
ev_tstamp interval
const char *path
都是隻讀,字面意義上的意思。
void ev_idle_init (ev_idle *, callback)
這個功能沒有研究過,暫記着把。
void ev_once (loop, int fd, int events, ev_tstamp timeout, callback)
從指定的f fd 中指定一個超時事件,這個函數的方便之處在於無需作 alloc
/conf
/start
/stop
/free
。
Fd 能夠小於0,這樣就沒有 I/O 監控,而且「events」會被忽略。
void ev_feed_event (loop, int fd, int revents);
向一個 fd 發送事件。須要注意的是,這個功能貌似是隻能在 loop 內調用纔有效,異步地在 loop 的另外一個線程直接調用是無效的。
void ev_feed_signal_event (loop, signum)
向一個 loop 模擬 signal。參見 ev_feed_signal
。
Create tcp echo server using libev
Listen
socketaccept
事件accept
調用read
注意:原文例子中未顯示的是,應當將 fd 設置爲非阻塞的。帶非阻塞設置的代碼以下:
some_init_func()
{
...
sd = socket (PF_INET, SOCK_STREAM, 0);
flags = fcntl (sd, F_GETEL, 0);
fcntl (sd, F_SETEL, flags | O_NONBLOCK);
bzero (&addr, sizeof(addr));
... // 設置 Address 和 port
bind (sd, (struct sockaddr *)(&addr), sizeof(addr));
...
}
some_init_func()
{
...
listen (sd, 2);
...
}
accept()
的 watchersome_init_func()
{
...
ev_io_init (&w_accept, accept_cb, sd, EV_READ);
ev_io_start (loop, &w_accept);
...
}
回調函數以下:
static void accept_cb (struct ev_loop *loop,
struct ev_io *watcher,
int revents)
{
...
client_sd = accept (watcher->fd, // accept() 調用,接受傳入鏈接
(struct sockaddr *)(&client_addr),
&client_len);
...
w_client = (struct ev_io *)malloc(sizeof(struct ev_io)); // 爲 read watcher 準備內存
...
ev_io_init (w_client, read_cb, client_sd, EV_READ); // 這裏就只示例 read 事件了。write 事件同理
ev_io_start (loop, w_client);
}
read()
的 callbackstatic void read_cb (struct ev_loop *loop,
struct ev_io *watcher,
int revents)
{
...
readCount = recv (watcher->fd, buffer, BUFFER_SIZE, 0); // 讀取的方法就視乎程序員的實現啦
send (watcher->fd, buffer, readCount, 0); // 把數據 echo 回去
...
}
原文例子使用的就是recv
/send
,實際上我我的偏心的是read
/write
ev_loop (loop, 0); // 這裏能夠直接使用 default loop
============== End