当前位置: 首页 > 工具软件 > libev > 使用案例 >

libev简介

黄高爽
2023-12-01

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 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);

TCP Server代码

#include <stdio.h>
#include <netinet/in.h>
#include <ev.h>
#define PORT_NO 3033
#define BUFFER_SIZE 1024
int total_clients = 0;  // Total number of connected clients
void 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 socket
if( (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 address
if (bind(sd, (struct sockaddr*) &addr, sizeof(addr)) != 0)
{
  perror("bind error");
}
// Start listing on the socket
if (listen(sd, 2) < 0)
{
  perror("listen error");
  return -1;
}
// Initialize and start a watcher to accepts client requests
ev_io_init(&w_accept, accept_cb, sd, EV_READ);
ev_io_start(loop, &w_accept);
// Start infinite loop
while (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 request
client_sd = accept(watcher->fd, (struct sockaddr *)&client_addr, &client_len);
if (client_sd < 0)
{
  perror("accept error");
  return;
}
total_clients ++; // Increment total_clients count
printf("Successfully connected with client.\n");
printf("%d client(s) connected.\n", total_clients);
// Initialize and start watcher to read client requests
ev_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 socket
read = 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 client
send(watcher->fd, buffer, read, 0);
bzero(buffer, read);
}

 类似资料: