【无标题】

1. libev库概述

libev 是一个高性能的事件循环/事件驱动库,用于 UNIX 系统(包括 Linux 和 macOS),它提供了异步事件处理的能力,如定时器I/O 事件信号子进程等。

2. libev事件循环与观察者模式

2.1.1 ev_loop

// ev_loop结构体的大致如下:
struct ev_loop {  
    // 当前时间戳  
    ev_tstamp ev_rt_now;  
  
    // 指向不同监听器集合的指针(也有可能是动态数组或链表)  
    ev_watcher **watchers[EV__COUNT]; // 假设是数组,用于存储不同类型的观察者  
  
    // 后端相关的字段  
    int backend; // 当前使用的后端类型  
    int backend_fd; // 后端使用的文件描述符  
  
    // 事件循环的状态标志  
    int flags; // 可能包含多种状态,如是否正在运行、是否应该停止等  
  
    // 触发队列,用于存储已经准备好但尚未处理的事件  
    // 注意:实际中这个部分可能会更复杂,因为libev支持多种优先级和事件调度策略  
    ev_watcher *pending[/* 假设的优先级数量 */];  
  
​
}; 

Libev的事件循环对象由struct ev_loop定义。

2.1.2 ev_run

开启事件循环。

ev_run()内部大致执行流程。

- 递增loop深度.
- 重置ev_break状态.
- 在第1次迭代之前,呼叫所有pending中的观察者。
开始循环(LOOP):
- 如果定义了EVFLAG_FORKCHECK宏, 则每次都检查fork.
- 如果检测到了fork, 则根据队列调用所有fork watcher.
- 根据队列调用所有prepare watcher.
- 如果调用了ev_break, 则直接结束事件循环.
- 如果fork已经被调用, 分离并且重新创建内核状态避免进程干扰.
- 更新未修改的内核状态.
- 更新event loop time (ev_now()).
- 如果有必要, 计算休眠和阻塞时间(EVRUN_NOWAIT或者没有活跃的观察者则不会导致sleep).
- 如果指定了I/O休眠时间, 则这里会执行.
- 递增循环迭代计数器.
- 阻塞进程等待事件来来临.
- 根据队列调用所有活跃I/O事件
- 更新event loop time(ev_now()) 避免时间跳跃.
- 根据队列处理超时定时器(ev_timer).
- 根据队列处理周期定时器(ev_periodic).
- 处理具有高优先级的idle事件.
- 根据队列处理所有(ev_check).
- signal、async、child 被作为I/O观察者实现并串行化执行.
- 如果调用ev_break或flags为EVRUN_ONCE、EVRUN_NOWAIT或没有活跃事件, 则结束事件循环. 否则重复上述步奏.
结束(FINISH):
- 根据情况重置EV_BREAK的状态.
- 递减loop深度
- ev_run返回.

2.3 使用实例

// 导入libev相关头文件
#include <ev.h>
​
int main (void)
{
  // 使用已定义的宏来获取默认的事件循环对象, 也可以根据自己的需求返回特定的ev_loop对象.
  struct ev_loop *loop = EV_DEFAULT;
​
  // 开始运行事件循环
  ev_run (loop, 0);
​
  // 如果事件循环退出, 那将会执行到这里.
  return 0;
}

2.2 ev_io

从宏中剥离出来ev_io结构体定义如下:

typedef struct ev_io
{
    int active;     //启动状态
    int pending;    //挂起状态
    int priority;   //优先级
    void *data;     //参数
    void (*cb)(struct ev_loop *loop, struct ev_io *w, int revents);//回调函数
    struct ev_watcher_list *next;//链表结构
    int fd;     /* 这里的fd,events就是派生类的私有成员,分别表示监听的文件fd和触发的事件(可读还是可写) */
    int events; 
} ev_io;

ev_io是这I/O观察者, 通过注册组合事件来监视状态变更。I/O观察者会在每次事件循环迭代中检查注册的文件描述符(fd)是否可读、可写。若I/O观察者观测到了注册的文件描述符可读时(假设注册文件描述符状态为EV_READ),会进入回调函数做出相应的处理。

2.2.1 使用实例

注册一个来自控制台(Stdin)的ev_io输入事件, 并将输其输出返回到控制台。

#include <ev.h>
#include <stdio.h>
#include <unistd.h>
​
//stdin观察者相对应的回调函数,当观察者检测到文件描述符发生相关事件的变化之后触发。
void stdin_cb(struct ev_loop *loop, ev_io *w, int revents) {
​
  char buffer[128];
  memset(buffer, 0, 128);
  read(w->fd, buffer,sizeof(buffer));
  puts(buffer);
  printf("等待输入: \n");
}
​
int main (void)
{
  // 使用已定义的宏来获取默认的事件循环对象, 也可以根据自己的需求返回特定的ev_loop对象.
  struct ev_loop *loop = EV_DEFAULT;
​
  //定义一个stdin观察者,用来监测文件描述符发生的变化。
  ev_io stdin_watcher;
​
  // 在启动一个I/O观察者之前, 需要先初始化它I/O观察者,并绑定相应回调处理函数。
  ev_io_init (&stdin_watcher, stdin_cb, 0, EV_READ);
    
  // 启动I/O观察者之后,若观察者检测到在stdin文件描述符变为可读后触发回调函数。
  ev_io_start (loop, &stdin_watcher);
  printf("等待输入: \n");
​
  // 开始运行事件循环
  ev_run (loop, 0);
​
  // 如果事件循环退出, 那将会执行到这里.
  return 0;
}

2.3 ev_timer

从宏中剥离出来ev_timer结构体定义如下:

typedef struct ev_watcher_time
{
    int active;     //启动状态
    int pending;    //挂起状态
    int priority;   //优先级
    void *data;     //参数
    void (*cb)(struct ev_loop *loop, struct ev_watcher_time *w, int revents);//回调函数
    
    ev_tstamp at;     /* 这个at就是派生类中新的自有成员 ,表示的是at时间触发 */
} ev_watcher_time;

ev_timer是相对计时器观察者, 通过设置指定的超时时间与可选的重复触发时间.

2.3.1 使用实例

创建一个3秒后超时, 之后每隔1秒超时的示例:

#include <ev.h>
#include <stdio.h>
#include <unistd.h>
​
// 当超时时间到达, 这个回调将会被触发.
static void timeout_cb (struct ev_loop *loop, ev_timer *w, int revents)
{
  puts ("timeout");
}
​
​
int main (void)
{
  // 可以使用已定义的宏来获取默认的事件循环, 当然你也可以根据自己的需求创建指定的.
  struct ev_loop *loop = EV_DEFAULT;
​
  ev_timer timeout_watcher;
  ev_timer_init (&timeout_watcher, timeout_cb, 3, 1);
  ev_timer_start (loop, &timeout_watcher);
​
  // 开始运行事件循环
  ev_run (loop, 0);
​
  // 如果事件循环退出, 那将会执行到这里.
  return 0;
}

2.4 ev_signal

从宏中剥离出来ev_signal结构体定义如下:

typedef struct ev_signal
{
    int active;     //启动状态
    int pending;    //挂起状态
    int priority;   //优先级
    void *data;     //参数
    void (*cb)(struct ev_loop *loop, struct ev_signal *w, int revents); //回调函数
    struct ev_watcher_list *next;  //链表结构
​
    int signum; /** 这个signum就是派生类中新的自有成员 ,表示的是接收到的信号 */
} ev_signal;

ev_signal 是将异步的信号转化为对文件描述符的同步处理。

注意:同一个信号(signal)可以用多个ev_signal观察者监视,但是这些观察者只能位于同一个事件循环中(ev_loop)。

2.4.1 使用实例

// 只需导入单个头文件
#include <ev.h>
#include <stdio.h>
​
// 当使用了键盘的组合键`CTRL`+`C`之后回调会被调用.
static void sig_cb (struct ev_loop *loop, ev_signal *w, int revents)
{
  puts ("结束信号已触发! Bye");
    
  //结束事件循环
  ev_break(loop, EVBREAK_ALL);
}
​
​
int main (void)
{
  // 可以使用已定义的宏来获取默认的事件循环, 当然你也可以根据自己的需求创建指定的.
  struct ev_loop *loop = EV_DEFAULT;
​
  ev_signal sigint;
  ev_signal_init(&sigint, sig_cb, SIGINT);
  ev_signal_start(loop, &sigint);
​
  // 开始运行事件循环
  ev_run (loop, 0);
​
  // 如果事件循环退出, 那将会执行到这里.
  return 0;
}

2.5 ev_async

ev_async观察者允许从其他线程或信号处理程序中“唤醒”事件循环,并请求执行一个回调函数。这种机制能够跨线程处理异步事件,不用在多个线程之间共享复杂的状态或数据结构。

2.5.1 使用实例

libev 中使用 ev_async 来处理跨线程的异步事件。

// 定义一个结构体来保存线程和事件循环之间的共享数据
typedef struct {
  struct ev_loop *loop;
  ev_async async_watcher;
  pthread_t thread_id;
} AsyncContext;
​
static void async_cb(struct ev_loop *loop, ev_async *w, int revents) {
  printf("Async callback called\n");
  ev_break(loop, EVBREAK_ALL);
}
​
static void *async_thread(void *arg) {
  AsyncContext *context = (AsyncContext *)arg;
​
  // 发送异步事件请求
  ev_async_send(context->loop, &context->async_watcher);
​
  // 异步线程完成任务后退出
  return NULL;
}
​
int main() {
  AsyncContext context;
  context.loop = EV_DEFAULT;
​
  // 初始化ev_async
  ev_async_init(&context.async_watcher, async_cb);
  ev_async_start(context.loop, &context.async_watcher);
​
  // 创建并启动异步处理线程
  pthread_create(&context.thread_id, NULL, async_thread, &context);
​
  // 运行事件循环,等待异步事件
  ev_run(context.loop, 0);
​
  return 0;
}

3.观察者状态

3.1 Initialised(已初始化状态)

1.在将观察者注册到时间循环之前,必须对其进行初始化。这种状体通过ev_TYPE_initev_signal_init ,ev_timer_init,ev_io_init)来设置观察者状态。

2.在初始化状态下,观察者在事件循环适宜的一块内存,可以随意移动、释放、重用等(前提是内存保持不变)

3.2 Started/Running/Active (已启动/正在运行/活动)

启用观察者(ev_TYPE_start),观察者会成为事件循环的属性,积极等待事件。在等待的条件下,观察者不能被访问、移动、释放。

3.3 Pending(挂起状态)

当观察者处于活动状态的时候(Active),并且检测到事件的变化,则观察者会进入挂起状态。

3.4 Stopped(停止状态)

在释放观察者之前,要保证观察者不是挂起态或者活动态 ,可以通过显示调用(ev_TYPE_stop)让观察者处于停止状态(初始化状态)。

4.观察者优先级模型

可以通过ev_set_priority设置观察者优先级,一次来决定观察者事件回调的调用顺序。

事件循环有两种优先级模型:

1.lock-out model(锁定模式):高优先级观察者接受到事件,低优先级监视器不会被调用。

2.only-for-ordering model(排序模式):在单个事件循环迭代中对回调调用进行排序:较高优先级的观察者在较低优先级的观察器之前被调用,但它们都在轮询新事件之前被调用。

Libev对除空闲观察者(ev_idle)(使用锁定模型)之外的所有观察者使用第二种(仅用于排序)模型。

4.1使用实例

#include <ev.h>  
#include <stdio.h>  
#include <stdlib.h>  
  
// 高优先级回调函数  
static void high_priority_cb(struct ev_loop *loop, ev_timer *w, int revents) {  
    printf("High Priority Timer: %f seconds\n", w->repeat);  
    // 可以在这里停止事件循环或者进行其他操作  
}  
  
// 低优先级回调函数  
static void low_priority_cb(struct ev_loop *loop, ev_timer *w, int revents) {  
    printf("Low Priority Timer: %f seconds\n", w->repeat);  
    // 可以在这里停止事件循环或者进行其他操作  
}  
  
int main() {  
    struct ev_loop *loop = EV_DEFAULT; // 使用默认事件循环  
  
    // 创建并初始化高优先级定时器  
    ev_timer high_priority_timer;  
    ev_timer_init(&high_priority_timer, high_priority_cb, 0., 1.0); // 1秒后触发  
    high_priority_timer.priority = 2; // 设置优先级为最高  
    ev_timer_start(loop, &high_priority_timer);  
  
    // 创建并初始化低优先级定时器  
    ev_timer low_priority_timer;  
    ev_timer_init(&low_priority_timer, low_priority_cb, 0., 2.0); // 2秒后触发  
    low_priority_timer.priority = -2; // 设置优先级为最低  
    ev_timer_start(loop, &low_priority_timer);  
  
    // 启动事件循环  
    ev_run(loop, 0);  
  
    return 0;  
}

5.libev的线程安全性和可重入性

5.1线程安全性

只要是操作不事件循环(ev_loop)实例,libev是线程安全的。但是如果对同一个ev_loop实例的并发调用,需要通过同步机制(如互斥锁、条件变量等)来确保一次只有一个线程可以调用。

5.2可重入性

可重入性指的是在单个线程中,可以在不同的栈帧安全地嵌套调用libev的函数。

  • 8
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值