libev安裝



轉載於http://www.cnblogs.com/wunaozai/p/3950249.html


Socket網絡編程不知不覺已經學了快兩個月了。現在是時候找個網絡庫學學了。搜索了很多關於如何學網絡編程的博客和問答。大致都是推薦學一個網絡庫,至於C++網絡庫有那麼幾個,各有各的好處。這裏就選這個代碼量少了,方便入門,等有一定的基礎後,再看看“學之者生,用之着死”的ace或者有可能成爲C++標準網絡庫的boost::asio,這個都是後話了。

  CentOS上安裝軟件最簡單的當然是yum安裝,我們可愛的libev好像現在還沒有,那我們只能通過源碼安裝了。地址: http://dist.schmorp.de/libev/libev-4.15.tar.gz

1 wget http://dist.schmorp.de/libev/libev-4.15.tar.gz
2 tar -zxf libev-4.15.tar.gz
3 cd libev-4.15
4 ./configure
5 make
6 make install

  下面這些是安裝信息

複製代碼
 1 Libraries have been installed in:
 2    /usr/local/lib
 3 
 4 If you ever happen to want to link against installed libraries
 5 in a given directory, LIBDIR, you must either use libtool, and
 6 specify the full pathname of the library, or use the `-LLIBDIR'
 7 flag during linking and do at least one of the following:
 8    - add LIBDIR to the `LD_LIBRARY_PATH' environment variable
 9      during execution
10    - add LIBDIR to the `LD_RUN_PATH' environment variable
11      during linking
12    - use the `-Wl,-rpath -Wl,LIBDIR' linker flag
13    - have your system administrator add LIBDIR to `/etc/ld.so.conf'
14 
15 See any operating system documentation about shared libraries for
16 more information, such as the ld(1) and ld.so(8) manual pages.
17 ----------------------------------------------------------------------
18  /bin/mkdir -p '/usr/local/include'
19  /usr/bin/install -c -m 644 ev.h ev++.h event.h '/usr/local/include'
20  /bin/mkdir -p '/usr/local/share/man/man3'
21  /usr/bin/install -c -m 644 ev.3 '/usr/local/share/man/man3'
複製代碼

   下面給出一個官方的例子

複製代碼
#include <stdio.h>
#include <ev.h> //ev庫頭文件

//定義一個ev_TYPE 的結構體
ev_io stdin_watcher;//定義一個stdin的觀測者
ev_timer timeout_watcher;


//所有的watcher的回調函數都有相似的特點
//當stdin有可讀的數據時,將會調用下面這個回調函數
static void stdin_cb(EV_P_ ev_io *w,int revents)
{
    puts("stdin ready");

    //每一次時間都必須用對應的停止函數,手動的停止其watcher
    ev_io_stop(EV_A_ w);
    //這將導致所有嵌套執行的ev_run停止監聽
    ev_break(EV_A_ EVBREAK_ALL);
}

//這是一個回調函數,用於定時器回調
static void timeout_cb(EV_P_ ev_timer *w,int revents)
{
    puts("timeout");
    //這將導致最早運行的ev_run停止監聽
    ev_break(EV_A_ EVBREAK_ONE);
}

int main(int argc,char **args)
{
    //使用一般默認的事件循環
    struct ev_loop *loop = EV_DEFAULT;

    //初始化一個I/O watcher,然後啓動它
    ev_io_init(&stdin_watcher,stdin_cb,0,EV_READ);
    ev_io_start(loop,&stdin_watcher);

    //初始化一個定時器watcher,然後啓動它,只有一次,沒有重複的5.5秒定時
    ev_timer_init(&timeout_watcher,timeout_cb,5.5,0);
    ev_timer_start(loop,&timeout_watcher);

    //這裏等待時間出發
    ev_run(loop,0);

    //撤銷監聽退出程序
    return 0;
}
複製代碼

  編譯 gcc server.c -lev -o server 就這樣是可以編譯通過的,但是執行就說找不到一個libev的庫。看了一下上面的安裝信息,才知道要設置一下環境變量。然後纔可以運行。(/etc/profile或~/.bashrc)

1 export LIBDIR=/usr/local/lib
2 export LD_LIBRARY_PATH=/usr/local/lib
3 export LD_RUN_PATH=/usr/local/lib

   用gcc -E選項編譯後的源代碼

複製代碼
 1 ev_io stdin_watcher;
 2 ev_timer timeout_watcher;
 3 
 4 
 5 
 6 
 7 static void stdin_cb(struct ev_loop *loop, ev_io *w,int revents)
 8 {
 9     puts("stdin ready");
10 
11 
12     ev_io_stop(loop, w);
13 
14     ev_break(loop, EVBREAK_ALL);
15 }
16 
17 
18 static void timeout_cb(struct ev_loop *loop, ev_timer *w,int revents)
19 {
20     puts("timeout");
21 
22     ev_break(loop, EVBREAK_ONE);
23 }
24 
25 int main(int argc,char **args)
26 {
27 
28     struct ev_loop *loop = ev_default_loop (0);
29 
30 
31     do { do { ((ev_watcher *)(void *)((&stdin_watcher)))->active = ((ev_watcher *)(void *)((&stdin_watcher)))->pending = 0; ( (ev_watcher *)(void *)(((&stdin_watcher))))->priority = (0); (((&stdin_watcher)))->cb = ((stdin_cb)); } while (0); do { ((&stdin_watcher))->fd = ((0)); ((&stdin_watcher))->events = ((EV_READ)) | EV__IOFDSET; } while (0); } while (0);
32     ev_io_start(loop,&stdin_watcher);
33 
34 
35     do { do { ((ev_watcher *)(void *)((&timeout_watcher)))->active = ((ev_watcher *)(void *)((&timeout_watcher)))->pending = 0; ( (ev_watcher *)(void *)(((&timeout_watcher))))->priority = (0); (((&timeout_watcher)))->cb = ((timeout_cb)); } while (0); do { ((ev_watcher_time *)((&timeout_watcher)))->at = ((5.5)); ((&timeout_watcher))->repeat = ((0)); } while (0); } while (0);
36     ev_timer_start(loop,&timeout_watcher);
37 
38 
39     ev_run(loop,0);
40 
41 
42     return 0;
43 }
複製代碼
複製代碼
 1     do {
 2         do {
 3             ((ev_watcher *)(void *)((&stdin_watcher)))->active = ((ev_watcher *)(void *)((&stdin_watcher)))->pending = 0;
 4             ( (ev_watcher *)(void *)(((&stdin_watcher))))->priority = (0);
 5             (((&stdin_watcher)))->cb = ((stdin_cb));
 6         } while (0);
 7         do {
 8             ((&stdin_watcher))->fd = ((0));
 9             ((&stdin_watcher))->events = ((EV_READ)) | EV__IOFDSET;
10         } while (0);
11     } while (0);
12     ev_io_start(loop,&stdin_watcher);
複製代碼

   Libev通過一個struct ev_loop結構表示一個事件驅動的框架。在這個框架裏面通過ev_xxx結構,ev_init、ev_xxx_set、ev_xxx_start接口箱這個事件驅動的框架裏面註冊事件監控器,當相應的事件監控器的事件出現時,便會觸發該事件監控器的處理邏輯,去處理該事件。處理完之後,這些監控器進入到下一輪的監控中。符合一個標準的事件驅動狀態的模型。

  Libev 除了提供了基本的三大類事件(IO事件、定時器事件、信號事件)外還提供了週期事件、子進程事件、文件狀態改變事件等多個事件,這裏我們用三大基本事件寫一個例子。

複製代碼
 1 #include <stdio.h>
 2 #include <signal.h>
 3 #include <string.h>
 4 #include <sys/unistd.h>
 5 #include <ev.h>
 6 
 7 
 8 void io_action(struct ev_loop *main_loop,ev_io *io_w,int e)
 9 {
10     int rst;
11     char buf[1024];
12     memset(buf,0,sizeof(buf));
13     puts("In IO action");
14     read(STDIN_FILENO,buf,sizeof(buf));
15     buf[1023]='\0';
16     printf("String: %s\n",buf);
17     ev_io_stop(main_loop,io_w);
18 }
19 
20 void timer_action(struct ev_loop *main_loop,ev_timer *time_w,int e)
21 {
22     puts("In Time action");
23     ev_timer_stop(main_loop,time_w);
24 }
25 
26 void signal_action(struct ev_loop *main_loop,ev_signal *signal_w,int e)
27 {
28     puts("In Signal action");
29     ev_signal_stop(main_loop,signal_w);
30     ev_break(main_loop,EVBREAK_ALL);
31 }
32 
33 int main(int argc,char **argv)
34 {
35     ev_io io_w;
36     ev_timer timer_w;
37     ev_signal signal_w;
38     struct ev_loop *main_loop = ev_default_loop(0);
39 
40     ev_init(&io_w,io_action);
41     ev_io_set(&io_w,STDIN_FILENO,EV_READ);
42 
43     ev_init(&timer_w,timer_action);
44     ev_timer_set(&timer_w,2,0);
45 
46     ev_init(&signal_w,signal_action);
47     ev_signal_set(&signal_w,SIGINT);
48 
49     ev_io_start(main_loop,&io_w);
50     ev_timer_start(main_loop,&timer_w);
51     ev_signal_start(main_loop,&signal_w);
52 
53     ev_run(main_loop,0);
54     return 0;
55 }
複製代碼

  該程序一直處於監聽狀態,直到有調用信號然後回調signal_w函數,該函數會調用ev_break函數退出ev_run的調用,如果註釋掉第30行的代碼,那麼程序會在調用三個回調函數後纔會結束(外包引用計數爲0),否則一直監聽着。具體ev_run和ev_break的參數說明如下:

void ev_run (EV_P_ int flags);

void ev_break (EV_P_ int how);

同樣我們這裏比較關注flags和how這兩個參數。flags有下面這幾個:

0:默認值。一直循環進行處理,直到外部引用計數==0或者是顯示退出。
EVRUN_NOWAIT:運行一次,poll時候不會等待。如果有pending事件進行處理,否則立即返回。
EVRUN_ONCE:運行一次,poll時候會等待至少一個event發生,處理完成之後返回。
而how有下面這幾個:

EVBREAK_ONE:只是退出一次ev_run這個調用。通常來說使用這個就可以了。
EVBREAK_ALL:退出所有的ev_run調用。這種情況存在於ev_run在pengding處理時候會遞歸調用。

  第38行創建一個struct ev_loop *結構體,上面我們給出 ev_default_loop(0) 進行創建。使用libev的核心是事件循環,可以用 ev_default_loop 或 ev_loop_new 函數創建循環,或者直接使用 EV_DEFAULT 宏,區別是 ev_default_loop 創建的事件循環不是線程安全的,而 ev_loop_new 創建的事件循環不能捕捉信號和子進程的觀察器。大多數情況下,可以像下面這樣使用:

複製代碼
if (!ev_default_loop (0))
    fatal ("could not initialise libev, bad $LIBEV_FLAGS in environment?");
//或者明確選擇一個後端:
struct ev_loop *epoller = ev_loop_new (EVBACKEND_EPOLL | EVFLAG_NOENV);
if (!epoller)
    fatal ("no epoll found here, maybe it hides under your chair");
//如果需要動態分配循環的話,建議使用 ev_loop_new 和 ev_loop_destroy 。
複製代碼

  在創建子進程後,且想要使用事件循環時,需要先在子進程中調用 ev_default_fork 或 ev_loop_fork 來重新初始化後端的內核狀態,它們分別對應 ev_default_loop 和 ev_loop_new 來使用。

  ev_run 啓動事件循環。它的第二個參數爲0時,將持續運行並處理循環直到沒有活動的事件觀察器或者調用了 ev_break 。另外兩個取值是 EVRUN_NOWAIT 和 EVRUN_ONCE 。

  ev_break 跳出事件循環(在全部已發生的事件處理完之後)。第二個參數爲 EVBREAK_ONE 或 EVBREAK_ALL 來指定跳出最內層的 ev_run 或者全部嵌套的 ev_run 。

  ev_suspend 和 ev_resume 用來暫停和重啓事件循環,比如在程序掛起的時候。

  接下來創建觀察器,它主要包括類型、觸發條件和回調函數。將它註冊到事件循環上,在滿足註冊的條件時,會觸發觀察器,調用它的回調函數。上面的例子中已經包含了IO觀察器和計時觀察器、信號觀察器,此外還有周期觀察器、文件狀態觀察器等等。初始化和設置觀察器使用 ev_init 和 ev_TYPE_set ,也可以直接使用 ev_TYPE_init 。在特定事件循環上啓動觀察器使用 ev_TYPE_start 。 ev_TYPE_stop 停止觀察器,並且會釋放內存。libev中將觀察器分爲4種狀態:初始化、啓動/活動、等待、停止。libev中的觀察器還支持優先級。

  下面將介紹各個觀察器(watcher)

  ev_io  獲取標準輸入

複製代碼
1 static void stdin_readable_cb (struct ev_loop *loop, ev_io *w, int revents)
2 {
3     ev_io_stop (loop, w);
4     //.. read from stdin here (or from w->fd) and handle any I/O errors
5 }
6 
7 ev_io stdin_readable;
8 ev_io_init (&stdin_readable, stdin_readable_cb, STDIN_FILENO, EV_READ);
9 ev_io_start (loop, &stdin_readable);
複製代碼

  ev_timer  創建一個 ?? 秒之後啓動的計時器

複製代碼
1 static void one_minute_cb (struct ev_loop *loop, ev_timer *w, int revents)
2 {
3     //創建一個60秒的計時器
4     //.. one minute over, w is actually stopped right here
5 }
6 
7 ev_timer mytimer;
8 ev_timer_init (&mytimer, one_minute_cb, 60., 0.);
9 ev_timer_start (loop, &mytimer);
複製代碼

  創建一個10s超時的超時器

複製代碼
 1 static void timeout_cb (struct ev_loop *loop, ev_timer *w, int revents)
 2 {
 3     //.. ten seconds without any activity
 4 }
 5 
 6 ev_timer mytimer;
 7 ev_timer_init (&mytimer, timeout_cb, 0., 10.); /* note, only repeat used */
 8 ev_timer_again (&mytimer); /* start timer */
 9 ev_run (loop, 0);
10 
11 // and in some piece of code that gets executed on any "activity":
12 // reset the timeout to start ticking again at 10 seconds
13 ev_timer_again (&mytimer);
複製代碼

  ev_periodic 創建一個小時爲單位的週期定時器

複製代碼
1 static void clock_cb (struct ev_loop *loop, ev_periodic *w, int revents)
2 {
3     // ... its now a full hour (UTC, or TAI or whatever your clock follows)
4 }
5 
6 ev_periodic hourly_tick;
7 ev_periodic_init (&hourly_tick, clock_cb, 0., 3600., 0);
8 ev_periodic_start (loop, &hourly_tick);
複製代碼

  自定義週期計算方式

複製代碼
1 #include <math.h>
2 
3 static ev_tstamp my_scheduler_cb (ev_periodic *w, ev_tstamp now)
4 {
5     return now + (3600. - fmod (now, 3600.));
6 }
7 
8 ev_periodic_init (&hourly_tick, clock_cb, 0., 0., my_scheduler_cb);
複製代碼

  如果想從當前時間開始

1 ev_periodic hourly_tick;
2 ev_periodic_init (&hourly_tick, clock_cb, fmod (ev_now (loop), 3600.), 3600., 0);
3 ev_periodic_start (loop, &hourly_tick);

  ev_signal 在收到 SIGINT 時做些清理

複製代碼
1 static void sigint_cb (struct ev_loop *loop, ev_signal *w, int revents)
2 {
3     ev_break (loop, EVBREAK_ALL);
4 }
5 
6 ev_signal signal_watcher;
7 ev_signal_init (&signal_watcher, sigint_cb, SIGINT);
8 ev_signal_start (loop, &signal_watcher);
複製代碼

  ev_child   fork 一個新進程,給它安裝一個child處理器等待進程結束

複製代碼
 1 ev_child cw;
 2 
 3 static void
 4 child_cb (EV_P_ ev_child *w, int revents)
 5 {
 6     ev_child_stop (EV_A_ w);
 7     printf ("process %d exited with status %x\n", w->rpid, w->rstatus);
 8 }
 9 
10 pid_t pid = fork ();
11 
12 if (pid < 0)
13     // error
14 else if (pid == 0)
15 {
16      // the forked child executes here
17      exit (1);
18 }
19 else
20 {
21     ev_child_init (&cw, child_cb, pid, 0);
22     ev_child_start (EV_DEFAULT_ &cw);
23 }
複製代碼

  ev_stat 文件狀態觀察器 

  監控/etc/passwd是否有變化

複製代碼
 1 static void passwd_cb (struct ev_loop *loop, ev_stat *w, int revents)
 2 {
 3     /* /etc/passwd changed in some way */
 4     if (w->attr.st_nlink)
 5     {
 6         printf ("passwd current size  %ld\n", (long)w->attr.st_size);
 7         printf ("passwd current atime %ld\n", (long)w->attr.st_mtime);
 8         printf ("passwd current mtime %ld\n", (long)w->attr.st_mtime);
 9     }
10     else
11         /* you shalt not abuse printf for puts */
12         puts ("wow, /etc/passwd is not there, expect problems. if this is windows, they already arrived\n");
13 }
14 
15 ...
16 ev_stat passwd;
17 
18 ev_stat_init (&passwd, passwd_cb, "/etc/passwd", 0.);
19 ev_stat_start (loop, &passwd);    
複製代碼

   下一小節就對那幾個常用的事件驅動類型進行簡單例子編寫。

 

  參考資料: http://wenku.baidu.com/view/957ea001b52acfc789ebc9bf.html

      : Libev手冊 http://cvs.schmorp.de/libev/ev.pod

      : http://my.oschina.net/u/917596/blog/176658

      : http://dirlt.com/libev.html

      : http://www.yeolar.com/note/2012/12/16/libev/

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章