libev简介

来源:互联网 发布:新顶级域名注册量 编辑:程序博客网 时间:2024/04/30 01:16

from http://ju.outofmemory.cn/entry/111430

简介

libev是Marc Lehmann用C写的高性能事件循环库。通过libev,可以灵活地把各种事件组织管理起来,如:时钟、io、信号等。libev在业界内也是广受好评,不少项目都采用它来做底层的事件循环

libev所实现的功能就是一个强大的reactor,可以notify的事件主要包括下面这些:

  • ev_io // IO可读可写
  • ev_stat // 文件属性变化
  • ev_async // 激活线程
  • ev_signal // 信号处理
  • ev_timer // 定时器
  • ev_periodic // 周期任务
  • ev_child // 子进程状态变化
  • ev_fork // 开辟进程
  • ev_cleanup // event loop退出触发事件
  • ev_idle // 每次event loop空闲触发事件
  • ev_embed // TODO:I have no idea.
  • ev_prepare // 每次event loop之前事件
  • ev_check // 每次event loop之后事件

Watcher介绍

watcher是Reactor中的Event Handler。一方面,它向事件循环提供了统一的调用接口(按类型区分);另一方面,它是外部代码的注入口,维护着具体的watcher信息,如:绑定的回调函数,watcher的优先级,是否激活等。

ev_io

ev_io的主要使命就是监听并响应指定文件描述fd上的读写事件。对fd的监听工作,主要委托给底层的io库来完成。libev对目前比较流行的io库都提供了支持,如:select, epoll以及windows的iocp等。在这里libev使用了Adaptor模式,通过统一的适配层隐藏了底层io库的细节。

ev_timer

ev_timer是主要负责处理超时事件的watcher。这类watcher被存储在loop->timers中,它们的特点是,超时时间小的watcher会被先触发。

ev_prepare

ev_prepare 在事件循环发生阻塞前会被触发。

ev_check

ev_check 在事件循环阻塞结束后会被触发。ev_check的触发是按优先级划分的。可以保证,ev_check是同一个优先级上阻塞结束后最先被触发的watcher。所以,如果要保证ev_check是最先被执行的,可以把它的优先级设成最高。

ev_io和ev_timer应该是libev中使用的最多的watcher了,也是比较典型的watcher。

Watcher示例代码:

ev_io

获取标准输入:

static voidstdin_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 voidone_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 voidtimeout_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 secondsev_timer_again (&mytimer);

ev_periodic

创建一个小时为单位的周期定时器:

static voidclock_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_tstampmy_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 voidsigint_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 voidchild_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 voidpasswd_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);

TCP Server代码

#include <stdio.h>#include <netinet/in.h>#include <ev.h>#define PORT_NO 3033#define BUFFER_SIZE 1024int total_clients = 0;  // Total number of connected clientsvoid accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents);void read_cb(struct ev_loop *loop, struct ev_io *watcher, int revents);int main(){struct ev_loop *loop = ev_default_loop(0);int sd;struct sockaddr_in addr;int addr_len = sizeof(addr);struct ev_io w_accept;// Create server socketif( (sd = socket(PF_INET, SOCK_STREAM, 0)) < 0 ){  perror("socket error");  return -1;}bzero(&addr, sizeof(addr));addr.sin_family = AF_INET;addr.sin_port = htons(PORT_NO);addr.sin_addr.s_addr = INADDR_ANY;// Bind socket to addressif (bind(sd, (struct sockaddr*) &addr, sizeof(addr)) != 0){  perror("bind error");}// Start listing on the socketif (listen(sd, 2) < 0){  perror("listen error");  return -1;}// Initialize and start a watcher to accepts client requestsev_io_init(&w_accept, accept_cb, sd, EV_READ);ev_io_start(loop, &w_accept);// Start infinite loopwhile (1){  ev_loop(loop, 0);}return 0;}/* Accept client requests */void accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents){struct sockaddr_in client_addr;socklen_t client_len = sizeof(client_addr);int client_sd;struct ev_io *w_client = (struct ev_io*) malloc (sizeof(struct ev_io));if(EV_ERROR & revents){  perror("got invalid event");  return;}// Accept client requestclient_sd = accept(watcher->fd, (struct sockaddr *)&client_addr, &client_len);if (client_sd < 0){  perror("accept error");  return;}total_clients ++; // Increment total_clients countprintf("Successfully connected with client.\n");printf("%d client(s) connected.\n", total_clients);// Initialize and start watcher to read client requestsev_io_init(w_client, read_cb, client_sd, EV_READ);ev_io_start(loop, w_client);}/* Read client message */void read_cb(struct ev_loop *loop, struct ev_io *watcher, int revents){char buffer[BUFFER_SIZE];ssize_t read;if(EV_ERROR & revents){  perror("got invalid event");  return;}// Receive message from client socketread = recv(watcher->fd, buffer, BUFFER_SIZE, 0);if(read < 0){  perror("read error");  return;}if(read == 0){  // Stop and free watchet if client socket is closing  ev_io_stop(loop,watcher);  free(watcher);  perror("peer might closing");  total_clients --; // Decrement total_clients count  printf("%d client(s) connected.\n", total_clients);  return;}else{  printf("message:%s\n",buffer);}// Send message bach to the clientsend(watcher->fd, buffer, read, 0);bzero(buffer, read);}

0 0
原创粉丝点击