libev庫的用法

libev是一個高性能的事件循環庫,比libevent庫的性能要好。Nodejs就是採用它作爲底層庫。libev的官方文檔在 這裏 ,文檔比較長。本文結合裏面的例子對它的用法做些簡單的總結。

例子

首先從官方的例子開始:

// a single header file is required
#include <ev.h>

#include <stdio.h> // for puts

// every watcher type has its own typedef'd struct
// with the name ev_TYPE
ev_io stdin_watcher;
ev_timer timeout_watcher;

// all watcher callbacks have a similar signature
// this callback is called when data is readable on stdin
static void
stdin_cb (EV_P_ ev_io *w, int revents)
{
  puts ("stdin ready");
  // for one-shot events, one must manually stop the watcher
  // with its corresponding stop function.
  ev_io_stop (EV_A_ w);

  // this causes all nested ev_run's to stop iterating
  ev_break (EV_A_ EVBREAK_ALL);
}

// another callback, this time for a time-out
static void
timeout_cb (EV_P_ ev_timer *w, int revents)
{
  puts ("timeout");
  // this causes the innermost ev_run to stop iterating
  ev_break (EV_A_ EVBREAK_ONE);
}

int
main (void)
{
  // use the default event loop unless you have special needs
  struct ev_loop *loop = EV_DEFAULT;

  // initialise an io watcher, then start it
  // this one will watch for stdin to become readable
  ev_io_init (&stdin_watcher, stdin_cb, /*STDIN_FILENO*/ 0, EV_READ);
  ev_io_start (loop, &stdin_watcher);

  // initialise a timer watcher, then start it
  // simple non-repeating 5.5 second timeout
  ev_timer_init (&timeout_watcher, timeout_cb, 5.5, 0.);
  ev_timer_start (loop, &timeout_watcher);

  // now wait for events to arrive
  ev_run (loop, 0);

  // break was called, so exit
  return 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中的觀察器還支持優先級。

不同類型的觀察器就不詳細解釋了,只把官方的一些例子貼在這裏吧。

ev_io

獲取標準輸入:

static void
stdin_readable_cb (struct ev_loop *loop, ev_io *w, int revents)
{
  ev_io_stop (loop, w);
  .. read from stdin here (or from w->fd) and handle any I/O errors
}

ev_io stdin_readable;
ev_io_init (&stdin_readable, stdin_readable_cb, STDIN_FILENO, EV_READ);
ev_io_start (loop, &stdin_readable);

ev_timer

創建一個60s之後啓動的計時器:

static void
one_minute_cb (struct ev_loop *loop, ev_timer *w, int revents)
{
  .. one minute over, w is actually stopped right here
}

ev_timer mytimer;
ev_timer_init (&mytimer, one_minute_cb, 60., 0.);
ev_timer_start (loop, &mytimer);

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

static void
timeout_cb (struct ev_loop *loop, ev_timer *w, int revents)
{
  .. ten seconds without any activity
}

ev_timer mytimer;
ev_timer_init (&mytimer, timeout_cb, 0., 10.); /* note, only repeat used */
ev_timer_again (&mytimer); /* start timer */
ev_run (loop, 0);

// and in some piece of code that gets executed on any "activity":
// reset the timeout to start ticking again at 10 seconds
ev_timer_again (&mytimer);

ev_periodic

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

static void
clock_cb (struct ev_loop *loop, ev_periodic *w, int revents)
{
  ... its now a full hour (UTC, or TAI or whatever your clock follows)
}

ev_periodic hourly_tick;
ev_periodic_init (&hourly_tick, clock_cb, 0., 3600., 0);
ev_periodic_start (loop, &hourly_tick);

或者自定義週期計算方式:

#include <math.h>

static ev_tstamp
my_scheduler_cb (ev_periodic *w, ev_tstamp now)
{
  return now + (3600. - fmod (now, 3600.));
}

ev_periodic_init (&hourly_tick, clock_cb, 0., 0., my_scheduler_cb);

如果想從當前時間開始:

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

ev_signal

在收到 SIGINT 時做些清理:

static void
sigint_cb (struct ev_loop *loop, ev_signal *w, int revents)
{
  ev_break (loop, EVBREAK_ALL);
}

ev_signal signal_watcher;
ev_signal_init (&signal_watcher, sigint_cb, SIGINT);
ev_signal_start (loop, &signal_watcher);

ev_child

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

ev_child cw;

static void
child_cb (EV_P_ ev_child *w, int revents)
{
  ev_child_stop (EV_A_ w);
  printf ("process %d exited with status %x\n", w->rpid, w->rstatus);
}

pid_t pid = fork ();

if (pid < 0)
  // error
else if (pid == 0)
  {
    // the forked child executes here
    exit (1);
  }
else
  {
    ev_child_init (&cw, child_cb, pid, 0);
    ev_child_start (EV_DEFAULT_ &cw);
  }

ev_stat

監控/etc/passwd是否有變化:

static void
passwd_cb (struct ev_loop *loop, ev_stat *w, int revents)
{
  /* /etc/passwd changed in some way */
  if (w->attr.st_nlink)
    {
      printf ("passwd current size  %ld\n", (long)w->attr.st_size);
      printf ("passwd current atime %ld\n", (long)w->attr.st_mtime);
      printf ("passwd current mtime %ld\n", (long)w->attr.st_mtime);
    }
  else
    /* you shalt not abuse printf for puts */
    puts ("wow, /etc/passwd is not there, expect problems. "
          "if this is windows, they already arrived\n");
}

...
ev_stat passwd;

ev_stat_init (&passwd, passwd_cb, "/etc/passwd", 0.);
ev_stat_start (loop, &passwd);

其他功能

libev還支持很多其他的有用的功能,比如自定義觀察器,在線程中使用等等,這些請看官方文檔。

Python綁定

libev提供了C和C++接口,很多其他語言也有對應的第三方接口。

libev的Python擴展是 pyev ,文檔見 http://packages.python.org/pyev/ 。

libev中的基礎功能在pyev中基本上都有對應的綁定。

官方給了兩個例子,如其中的展示基本用法的例子:

import signal
import pyev

def sig_cb(watcher, revents):
    print("got SIGINT")
    loop = watcher.loop
    # optional - stop all watchers
    if loop.data:
        print("stopping watchers: {0}".format(loop.data))
        while loop.data:
            loop.data.pop().stop()
    # unloop all nested loop
    print("stopping the loop: {0}".format(loop))
    loop.stop(pyev.EVBREAK_ALL)

def timer_cb(watcher, revents):
    watcher.data += 1
    print("timer.data: {0}".format(watcher.data))
    print("timer.loop.iteration: {0}".format(watcher.loop.iteration))
    print("timer.loop.now(): {0}".format(watcher.loop.now()))

if __name__ == "__main__":
    loop = pyev.default_loop()
    # initialise and start a repeating timer
    timer = loop.timer(0, 2, timer_cb, 0)
    timer.start()
    # initialise and start a Signal watcher
    sig = loop.signal(signal.SIGINT, sig_cb)
    sig.start()
    loop.data = [timer, sig] # optional
    # now wait for events to arrive
    loop.start()
        </div>
            </div>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章