一、事件处理框架(event_base)

1.  event_base

     使用 libevent 函数之前需要分配一个或者多个 event_base 结构体。每个event_base 结构体持有一个事件集合,可以检测以确定哪个事件是激活的。

  • 相当于epoll红黑树的树根

  • 抽象层, 完成对event_base的封装
  • 每个 event_base 都有一种用于检测哪种事件已经就绪的 ”方法“,或者说后端。

Linux C编程之十九(2) libevent

2.  相关函数

(1)创建event_base

struct event_base* event_base_new(void);
失败返回NULL

(2)释放event_base

event_base_free(struct event_base* base);

(3)循环监听base对应的事件, 等待条件满足

event_base_dispatch();

(4)查看event_base封装的后端

const char **event_get_supported_methods(void);
char* str[];
const char * event_base_get_method(
    const struct event_base *base
);

注意:event_base和fork

  • 子进程创建成功之后, 父进程可以继续使用event_base
  • 子进程中需要继续使用event_base需要重新进程初始化
int event_reinit(struct event_base* base);

二、事件循环(event_loop) 

1. 事件处理方式

    一旦有了一个已经注册了某些事件的 event_base, 就需要让 libevent 等待事件并且通知事件的发生。

#define EVLOOP_ONCE                  0x01
    事件只会被触发一次
    事件没有被触发, 阻塞等
#define EVLOOP_NONBLOCK              0x02
    非阻塞 等方式去做事件检测
    不关心事件是否被触发了
#define EVLOOP_NO_EXIT_ON_EMPTY      0x04
    没有事件的时候, 也不退出轮询检测

2. 相关函数

 (1)int event_base_loop(struct event_base *base, int flags);
          正常退出返回0, 失败返回-1
 (2)int event_base_dispatch(struct event_base* base);

  • 等同于没有设置标志的 event_base_loop ( )
  • 将一直运行,直到没有已经注册的事件了,或者调用 了event_base_loopbreak()或者 event_base_loopexit()为止。

3. 循环停止

(1)如果 event_base 当前正在执行激活事件的回调 ,它将在执行完当前正在处理的事件后立即退出

int event_base_loopexit(
    struct event_base *base,
    const struct timeval *tv
);

(2)让event_base 立即退出循环

int event_base_loopbreak(struct event_base *base);

返回值: 成功 0, 失败 -1

    其中:

struct timeval {
    long tv_sec; 
    long tv_usec; 
};

三、事件创建 (event) 

1. 创建新事件

#define  EV_TIMEOUT              0x01    // 废弃
#define  EV_READ                 0x02
#define  EV_WRITE                0x04
#define  EV_SIGNAL               0x08
#define  EV_PERSIST              0x10    // 持续触发
#define  EV_ET                   0x20    // 边沿模式

typedef void (*event_callback_fn)(evutil_socket_t, short, void *); 
struct event *event_new(
    struct event_base *base, 
    evutil_socket_t fd, // 文件描述符 - int
    short what, 
    event_callback_fn cb, // 事件的处理动作
    void *arg
); 

Linux C编程之十九(2) libevent

 注意:调用event_new()函数之后,新事件处于已初始化和非未决状态 。 

2. 释放事件

void event_free(struct event *event);

3. 设置未决事件

     构造事件之后,在将其添加到 event_base 之前实际上是不能对其做任何操作的。使用event_add()将事件添加到event_base, 非未决事件 -> 未决事件。

int event_add(
    struct event *ev, 
    const struct timeval *tv
); 

参数:

     ev:创建的事件

 tv: 
   NULL: 事件被触发, 对应的回调被调用
   tv = {0, 100}, 如果设置的时间,在改时间段内检测的事件没被触发, 时间到达之后, 回调函数还是会被调用

返回值:

    函数调用成功返回 0,失败返回 -1 。

 4. 设置非未决

int event_del(struct event *ev); 

对已经初始化的事件调用 event_del() 将使其成为非未决和非激活的。如果事件不是未决的或者激活的,调用将没有效果。

返回值:

           成功时函数返回 0,失败时返回-1。

5. 事件的状态转换

Linux C编程之十九(2) libevent

 6. 示例

    通过 libevent 实现进程间通过管道来通信:

 1 #include <stdio.h>
 2 #include <unistd.h>
 3 #include <stdlib.h>
 4 #include <sys/types.h>
 5 #include <sys/stat.h>
 6 #include <string.h>
 7 #include <fcntl.h>
 8 #include <event2/event.h>
 9 
10 // 对操作处理函数
11 void write_cb(evutil_socket_t fd, short what, void *arg)
12 {
13     // write管道
14     char buf[1024] = {0};
15     static int num = 0;
16     sprintf(buf, "hello, world == %d\n", num++);
17     write(fd, buf, strlen(buf)+1);
18 }
19 
20 
21 // 写管道
22 int main(int argc, const char* argv[])
23 {
24     // open file
25     int fd = open("myfifo", O_WRONLY | O_NONBLOCK);
26     if(fd == -1)
27     {
28         perror("open error");
29         exit(1);
30     }
31 
32     // 写管道
33     struct event_base* base = NULL;
34     base = event_base_new();
35 
36     // 创建事件
37     struct event* ev = NULL;
38     // 检测的写缓冲区是否有空间写
39     ev = event_new(base, fd, EV_WRITE | EV_PERSIST, write_cb, NULL);
40 
41     // 添加事件
42     event_add(ev, NULL);
43 
44     // 事件循环
45     event_base_dispatch(base);
46 
47     // 释放资源
48     event_free(ev);
49     event_base_free(base);
50     close(fd);
51     
52     return 0;
53 }
write_fifo.c

相关文章: