libev是一个**事件驱动库**,它需要循环探测事件是否发生,在Linux上实际是封装了epoll等系统调用。
其循环过程由ev_loop( )函数设置,循环体是ev_loop结构。
//创建事件循环
void ev_loop( ev_loop* loop, int flags )
libev支持多种事件类型,常用的如I/O,定时器,信号等。详细的可以去看官网教程的WATCHER TYPES章节。
每种事件都有具体结构来表示,如ev_io表示一个I/O事件。事件通过ev_io_init()函数初始化。
//初始化一个事件
void ev_io_init(ev_io *io, callback, int fd, int events)
初始化内容包括了,探测的fd,事件类型,回调函数等。如初始化一个fdx的可读事件:
ev_io_init(accept_event, acceept_callback, fdx, EV_READ);
普通网络程序中,一般阻塞在accept(socket_fd),在连接到达后返回一个connect_fd继续等待。而事件驱动模型中,则变为探测socket_fd是否可读。在callback回调中去执行accept,不再阻塞。
现在我们要做的只是将初始化好的accept_event这个事件加入到事件循环中去。
将事件添加到事件循环,或将事件从事件循环删去,是通过ev_io_start()和ev_io_stop函数完成(这里示例I/O事件):
void ev_io_start( ev_loop *loop, ev_io* io )
void ev_io_stop( EV_A_* )
虽然很多细节未考虑,但一个简单的事件驱动的服务器模型已经有了。其中屏蔽了很多裸写epoll要处理的细节。
简单总结下步骤:
1、创建一个事件循环
struct ev_loop *loop = EV_DEFAULT;
2、初始化一个待探测的事件,这个事件由fd(I/O为例),回调函数,事件类型(读/写)等组成
ev_io stdin_watcher;
ev_io_init (&stdin_watcher, callback, /*STDIN_FILENO*/ 0, EV_READ);
3、将事件加入到事件循环
ev_io_start (loop, &stdin_watcher);
4、启动事件循环
ev_run (loop, 0);
我们可以随时向事件循环中加入事件,或从中删除某事件。
完整示例:
// 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; }