Socket網絡編程--Libev庫學習(3)


  這一小節繼續講解各個觀察器(Watcher).

  上一小節已經講解了ev_io(IO可讀可寫觀察器),ev_stat(文件屬性變化觀察器),ev_signal(信號處理觀察器),ev_timer(定時器),ev_periodic(周期任務處理),ev_child(子進程狀態變化觀察器)。這一小節准備講ev_fork(創建的進程時的觀察器),ev_async(異步調用觀察器),ev_cleanup(event loop退出時觸發事件),ev_prepare(每次event loop之前事件),ev_check(每次event loop之后事件),ev_idle(每次event loop空閑觸發事件).

  ev_async  (ev_async當ev_async_send通過watcher調用時調用,觸發EV_ASYNC)

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <unistd.h>
 4 #include <stdlib.h>
 5 #include <ev.h>
 6 
 7 ev_async async_watcher;
 8 
 9 static void sigint_callback(struct ev_loop *loop,ev_signal * w,int revents)
10 {
11     if(revents & EV_SIGNAL)
12     {
13         printf("Call sigint_callback\n");
14         printf("ev_async_send 調用前 %d\n",ev_async_pending(&async_watcher));
15         ev_async_send(loop,&async_watcher);//這里會調用async_callback
16         printf("ev_async_send 調用后 %d\n",ev_async_pending(&async_watcher));
17     }
18 }
19 
20 static void sigquit_callback(struct ev_loop *loop,ev_signal *w,int revetns)
21 {
22     printf("Call sigquit_callback\n");
23     ev_break(loop,EVBREAK_ALL);
24 }
25 
26 static void async_callback(struct ev_loop *loop,ev_async *w,int revents)
27 {
28     if(revents & EV_ASYNC)
29     {
30         printf("Call async_callback\n");
31     }
32 }
33 
34 int main(int argc, char **args)
35 {
36     struct ev_loop *main_loop=ev_default_loop(0);
37 
38     ev_init(&async_watcher,async_callback);
39     ev_async_start(main_loop,&async_watcher);
40 
41     ev_signal sigint_watcher;
42     ev_init(&sigint_watcher,sigint_callback);
43     ev_signal_set(&sigint_watcher,SIGINT);
44     ev_signal_start(main_loop,&sigint_watcher);
45 
46     ev_signal sigquit_watcher;//這里的ev_signal不能與上面共用,必須在聲明一個變量
47     ev_init(&sigquit_watcher,sigquit_callback);
48     ev_signal_set(&sigquit_watcher,SIGQUIT);
49     ev_signal_start(main_loop,&sigquit_watcher);
50 
51     ev_run(main_loop,0);
52     return 0;
53 }

  下面這個是運行截圖

  可以看出程序ev_async這個是通過ev_async_send來驅動async_callback這個回調函數執行的。而且ev_async_pending這個也是可以判斷ev_async是否處於pending狀態。我在第15行處增加了sleep(1)后,運行的結構還是一樣,可以看出ev_async所綁定的回調函數是處於pending狀態而不是另外開一個線程(進程)來執行的。

  從上面的處理機制看,好像就是發送一個信號,然后ev_async就調用回調函數去執行,好像跟ev_signal一樣,沒有什么特點啊。這里給出官方文檔的解釋。

This functionality is very similar to ev_signal watchers, as signals, too, are asynchronous in nature, and signals, too, will be compressed (i.e. the number of callback invocations may be less than the number of ev_async_sent calls).

Unlike ev_signal watchers, ev_async works with any event loop, not just the default loop.

  上面說到,不同與ev_signal watchers的是,ev_async可以在多種event loop,而不是默認的loop。前幾小節已經講到ev_loop 的創建,可以通過ev_loop_new進行創建(創建異步事件什么的)。具體的用法這里還不是很清楚,先跳過。

  ev_fork (開辟進程時觀察器)

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <unistd.h>
 4 #include <stdlib.h>
 5 #include <ev.h>
 6 
 7 static void fork_callback(struct ev_loop *loop,ev_fork *w,int revents)
 8 {
 9     printf("Call fork_callback\n");
10 }
11 
12 static void timeout_callback(struct ev_loop*loop,ev_timer *w,int revents)
13 {
14     printf("Time Out\n");
15     ev_break(loop,EVBREAK_ALL);
16 }
17 
18 int main(int argc, char **args)
19 {
20     struct ev_loop *main_loop=ev_default_loop(0);
21 
22     ev_fork fork_watcher;
23     ev_init(&fork_watcher,fork_callback);
24     ev_fork_start(main_loop,&fork_watcher);
25 
26     ev_timer timer_watcher;
27     ev_init(&timer_watcher,timeout_callback);
28     ev_timer_set(&timer_watcher,3,0);
29     ev_timer_start(main_loop,&timer_watcher);
30 
31     switch(fork())
32     {
33         case -1:
34             break;
35         case 0://child
36             //ev_loop_fork(main_loop);
37             break;
38     }
39 
40     ev_run(main_loop,0);
41     return 0;
42 }

  上面這個代碼沒有輸出Call fork_callback,如果把第36行的注釋去掉,就會輸出Call fork_callback了,可以說明這個ev_fork不是針對fork函數創建的進程。而是要ev_loop_fork針對ev_loop 創建的loop。具體解釋的不是很清楚,下面給出官方文檔:

Fork watchers are called when a fork () was detected (usually because whoever is a good citizen cared to tell libev about it by calling ev_default_fork or ev_loop_fork). The invocation is done before the event loop blocks next and before ev_check watchers are being called, and only in the child after the fork. If whoever good citizen calling ev_default_fork cheats and calls it in the wrong process, the fork handlers will be invoked, too, of course.

  loop的創建小解

 1 struct ev_loop *ev_default_loop (unsigned int flags) 
 2 struct ev_loop *ev_loop_new (unsigned int flags) 
 3 //這兩個函數都是默認原始化一個loop,區別是第一個不是線程安全的,第二個不能捕捉信號和子進程的watcher。 
 4 //參數flags可以為下面幾種類型: 
 5 //引用
 6 #define EVFLAG_AUTO        0x00000000U /* not quite a mask */ 
 7 /* flag bits */ 
 8 #define EVFLAG_NOENV       0x01000000U /* do NOT consult environment */ 
 9 #define EVFLAG_FORKCHECK   0x02000000U /* check for a fork in each iteration */ 
10 /* method bits to be ored together */ 
11 #define EVBACKEND_SELECT   0x00000001U /* about anywhere */ 
12 #define EVBACKEND_POLL     0x00000002U /* !win */ 
13 #define EVBACKEND_EPOLL    0x00000004U /* linux */ 
14 #define EVBACKEND_KQUEUE   0x00000008U /* bsd */ 
15 #define EVBACKEND_DEVPOLL  0x00000010U /* solaris 8 */ /* NYI */ 
16 #define EVBACKEND_PORT     0x00000020U /* solaris 10 */
17 ev_default_fork () 
18 ev_loop_fork (loop)
19 //這兩個函數就是當你在子進程里須要 運用 libev的函數的之前必須要調用。他們的區別是第二個函數是當運用 ev_loop_new建立 的loop時,才用第二個函數,也就是說重用父進程建立 的loop。 
20 ev_loop (loop, int flags)
21 //開始事件循環。 
22 ev_TYPE_init (ev_TYPE *watcher, callback, [args]) 
23 //原始化一個watcher。TYPE也就是libev支持的事件類型,比如io,比如time等等。。 
24 //第一個參數為一個watcher,第二個回調函數,第三個句柄,第四個事件類型。包含下面幾種: 
25 //引用
26 #define EV_UNDEF     -1 /* guaranteed to be invalid */ 
27 #define EV_NONE      0x00 /* no events */ 
28 #define EV_READ      0x01 /* ev_io detected read will not block */ 
29 #define EV_WRITE     0x02 /* ev_io detected write will not block */ 
30 #define EV_IOFDSET   0x80 /* internal use only */ 
31 #define EV_TIMEOUT   0x00000100 /* timer timed out */ 
32 #define EV_PERIODIC  0x00000200 /* periodic timer timed out */ 
33 #define EV_SIGNAL    0x00000400 /* signal was received */ 
34 #define EV_CHILD     0x00000800 /* child/pid had status change */ 
35 #define EV_STAT      0x00001000 /* stat data changed */ 
36 #define EV_IDLE      0x00002000 /* event loop is idling */ 
37 #define EV_PREPARE   0x00004000 /* event loop about to poll */ 
38 #define EV_CHECK     0x00008000 /* event loop finished poll */ 
39 #define EV_EMBED     0x00010000 /* embedded event loop needs sweep */ 
40 #define EV_FORK      0x00020000 /* event loop resumed in child */ 
41 #define EV_ASYNC     0x00040000 /* async intra-loop signal */ 
42 #define EV_ERROR     0x80000000 /* sent when an error occurs */ 
43 //引用
44 ev_TYPE_start (loop *, ev_TYPE *watcher)
45 //啟動一個watcher。 

 

 1     switch(fork())
 2     {
 3         case -1:
 4             break;
 5         case 0://child
 6             ev_loop_fork(main_loop);//使用父進程main_loop
 7             ev_timer timer_watcher1;
 8             ev_init(&timer_watcher1,timeout_callback);
 9             ev_timer_set(&timer_watcher1,3,0);
10             ev_timer_start(main_loop,&timer_watcher1);
11             break;
12     }

  上面代碼如果修改如下是可以編譯通過的。就是在父進程中的main_loop中再增加一個watcher,這個程序將會輸出三次Time Out。注意如果沒有第六行的ev_loop_fork是編譯不通過的。大概的原因是ev庫設計的原因。

  下面給出一個例子,用於以后可以參考用

 1 #include <ev.h>
 2 #include <stdio.h>
 3 
 4 //不同的watcher
 5 ev_io stdin_watcher;
 6 ev_timer timeout_watcher;
 7 ev_timer timeout_watcher_child;
 8 
 9 //標准輸入的回調函數
10 static void stdin_cb (EV_P_ ev_io *w, int revents)
11 {
12     puts ("stdin ready");
13     ev_io_stop (EV_A_ w);
14     ev_unloop (EV_A_ EVUNLOOP_ALL);
15 }
16 
17 //父進程的定時器回調函數
18 static void timeout_cb (EV_P_ ev_timer *w, int revents)
19 {
20     puts ("timeout");
21     ev_unloop (EV_A_ EVUNLOOP_ONE);
22 }
23 //子進程的定時器回調函數
24 static void timeout_cb_child (EV_P_ ev_timer *w, int revents)
25 {
26     puts ("child timeout");
27     ev_unloop (EV_A_ EVUNLOOP_ONE);
28 }
29 
30 static void fork_callback(struct ev_loop *loop,ev_fork *w,int revents)
31 {
32     printf("Call fork_callback\n");
33 }
34 
35 int main (void)
36 {
37     //創建一個backend為select的loop
38     struct ev_loop *loop = ev_loop_new(EVBACKEND_SELECT);
39 
40     //初始化並啟動父進程的watcher
41     ev_timer_init(&timeout_watcher, timeout_cb, 10, 0.);
42     ev_timer_start(loop, &timeout_watcher);
43     switch (fork()) {
44         case -1:
45             return -1;
46         case 0:
47             //使用父進程loop。
48             ev_loop_fork(loop);
49             //子進程的loop
50             struct ev_loop *loop_child = ev_loop_new (EVBACKEND_SELECT);
51             ev_io_init (&stdin_watcher, stdin_cb, /*STDIN_FILENO*/ 0, EV_READ);
52             ev_io_start (loop, &stdin_watcher);
53             ev_timer_init(&timeout_watcher_child, timeout_cb_child, 5.5, 0.);
54             ev_timer_start(loop_child, &timeout_watcher_child);
55             ev_loop(loop_child,0);
56     }
57 
58     //等待事件
59     ev_loop (loop, 0);
60     return 0;
61 }

  ev_cleanup  event loop 退出觸發事件

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <unistd.h>
 4 #include <stdlib.h>
 5 #include <ev.h>
 6 
 7 struct ev_loop *main_loop;
 8 
 9 static void program_exits(void)
10 {
11     printf("Call AtExit\n");
12     ev_loop_destroy(EV_DEFAULT);//注釋掉43行處代碼,該函數在這里沒有調用到cleanup_callback,但是卻執行沒有錯誤
13 }
14 
15 static void cleanup_callback(struct ev_loop *loop,ev_cleanup *w,int revents)
16 {
17     printf("Call cleanup_callback\n");
18 }
19 
20 static void timer_callback(struct ev_loop *loop,ev_timer *w,int revents)
21 {
22     printf("Call timer_callback\n");
23 }
24 
25 int main(int argc, char **args)
26 {
27     //struct ev_loop *main_loop=ev_default_loop(0);//error 注意ev_loop_destroy與ev_loop_new對應
28     main_loop=ev_loop_new(EVBACKEND_EPOLL);
29 
30     ev_cleanup cleanup_watcher;
31     ev_init(&cleanup_watcher,cleanup_callback);
32     ev_cleanup_start(main_loop,&cleanup_watcher);
33 
34     ev_timer timer_watcher;
35     ev_init(&timer_watcher,timer_callback);
36     ev_timer_set(&timer_watcher,0.2,0);
37     ev_timer_start(main_loop,&timer_watcher);
38 
39     atexit(program_exits);
40 
41     ev_run(main_loop,0);
42 
43     ev_loop_destroy(main_loop);//在這里就可以調用到cleanup_callback
44     printf("END\n");
45     return 0;
46 }

  運行時截圖

  ev_prepare  (每次event loop之前事件)

  ev_check  (每次event loop之后事件)

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <unistd.h>
 4 #include <stdlib.h>
 5 #include <ev.h>
 6 
 7 static void prepare_callback(struct ev_loop *loop,ev_prepare *w,int revents)
 8 {
 9     printf("Prepare Callback\n");
10 }
11 
12 static void check_callback(struct ev_loop *loop,ev_check *w,int revents)
13 {
14     printf("Check Callback\n");
15 }
16 
17 static void timer_callback(struct ev_loop *loop,ev_timer *w,int revents)
18 {
19     printf("Timer Callback\n");
20 }
21 
22 static void sigint_callback(struct ev_loop *loop,ev_signal *w,int revents)
23 {
24     printf("Sigint Callback\n");
25     ev_break(loop,EVBREAK_ALL);
26 }
27 
28 int main(int argc, char **args)
29 {
30     struct ev_loop *main_loop=ev_default_loop(0);
31 
32     ev_prepare prepare_watcher;
33     ev_check check_watcher;
34     ev_timer timer_watcher;
35     ev_signal signal_watcher;
36 
37     ev_prepare_init(&prepare_watcher,prepare_callback);
38     ev_check_init(&check_watcher,check_callback);
39     ev_timer_init(&timer_watcher,timer_callback,2,0);
40     ev_signal_init(&signal_watcher,sigint_callback,SIGINT);
41 
42     ev_prepare_start(main_loop,&prepare_watcher);
43     ev_check_start(main_loop,&check_watcher);
44     ev_timer_start(main_loop,&timer_watcher);
45     ev_signal_start(main_loop,&signal_watcher);
46 
47     ev_run(main_loop,0);
48     return 0;
49 }

  運行結果

  看前三個為一組,我測試了幾次都是這樣,Timer Callback的輸出都是在Check之后,這個不知道為什么不過后面的捕獲SIGINT信號就沒有這個問題,SIGINT信號的回調函數的輸出是處於Prepare和Check之間。這個就符合預想。還有就是我們輸入一個Ctrl-C時,也會觸發Prepare-Check的回調函數。這個倒是沒有想到,應該是一個ev_signal會向ev_loop里放入兩個處理過程,一個是Linux默認的捕獲SIGINT信號(signal函數)一個是我們的回調函數,大概是在默認的回調函數中調用我們的回調函數,畢竟捕獲信號是系統調用。上面這個是我的猜想(理解),不一定是正確的。

  ev_idle (每次event loop空閑觸發事件)

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <unistd.h>
 4 #include <stdlib.h>
 5 #include <ev.h>
 6 
 7 ev_idle idle_watcher;
 8 int count=0;
 9 
10 static void timer_callback_start(struct ev_loop *loop,ev_timer *w,int revents)
11 {
12     printf("Timer Callback Start\n");
13     ev_idle_start(loop,&idle_watcher);
14 }
15 static void timer_callback_stop(struct ev_loop *loop,ev_timer *w,int revents)
16 {
17     printf("Timer Callback Stop\n");
18     ev_idle_stop(loop,&idle_watcher);
19 }
20 
21 static void sigint_callback(struct ev_loop *loop,ev_signal *w,int revents)
22 {
23     printf("Sigint Callback\n");
24     ev_break(loop,EVBREAK_ALL);
25 }
26 
27 static void idle_callback(struct ev_loop *loop,ev_idle *w,int revents)
28 {
29     count++;
30 }
31 
32 int main(int argc, char **args)
33 {
34     struct ev_loop *main_loop=ev_default_loop(0);
35 
36     ev_timer timer_watcher_start;
37     ev_timer timer_watcher_stop;
38     ev_signal signal_watcher;
39 
40     ev_idle_init(&idle_watcher,idle_callback);
41     ev_timer_init(&timer_watcher_start,timer_callback_start,1,0);
42     ev_timer_init(&timer_watcher_stop,timer_callback_stop,3,0);
43     ev_signal_init(&signal_watcher,sigint_callback,SIGINT);
44 
45     ev_timer_start(main_loop,&timer_watcher_start);
46     ev_timer_start(main_loop,&timer_watcher_stop);
47     ev_signal_start(main_loop,&signal_watcher);
48 
49     ev_run(main_loop,0);
50 
51     printf("從第1秒到第3秒之間count計數器的累加到 %d\n",count);
52     return 0;
53 }

  運行結果

  我們的idle是可以控制開始和結束的。而這個idle的作用是但event_loop處於空閑的時候,與其在ev_run阻塞等待,不如利用這時的cpu時間來做其他事。應用的話,就是如果服務器繁忙的話就主要處理請求等,如果服務器請求不多時,可以利用cpu時間來處理備份什么的,這樣就可以最大限度的利用cpu了。

  觀察器watcher差不多就這些了,還有個ev_embed這個還不會用。

 

  參考資料: http://wangjunle23.blog.163.com/blog/static/11783817120124308920321/

        : http://simohayha.iteye.com/blog/306712

  本文地址: http://www.cnblogs.com/wunaozai/p/3955156.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM