- Linux服务器必须处理的三类时间:IO事件、信号、定时事件
- 处理这三类事件必须考虑三个问题:
- 统一事件源:使用IO复用系统来管理所有事件
- 可移植性:不同操作系统有不同的IO复用方式,Solaris的dev/poll,FressBSD的kqueue,Linux的epoll。
- 对并发编程的支持。
12.1 I/O框架库概述
- I/O框架库以库函数的心事,封装了较为底层的调用,给应用系统提供了一组更便捷的接口。
-
基于Reactor模式的I/O框架库包含:
-
1. 句柄
- 由于IO框架库需要处理IO事件、信号和定时事件被称为统一事件源,一个
句柄
绑定了一个事件源
. - 对于IO事件,句柄在Linux中的具体表现是
文件描述符
- 对于信号,句柄在Linux中的具体表现是
信号值
- 当内核检测到就绪事件后,它会通过句柄来通知应用程序。
- 由于IO框架库需要处理IO事件、信号和定时事件被称为统一事件源,一个
-
事件多路分发器[EventDemultiplexer]
- 事件的到来是随机的、异步的。
- IO框架库会将系统支持的各种IO复用系统调用封装成统一的接口,称为事件多路分发器。
- demultiplex方法是等待事件的核心函数,内部调用是select、poll和epoll_wait等函数。
- register_event:从事件多路分发器添加事件
- remove_event:从事件多路分发器删除事件
-
事件处理器和具体事件处理器
- 事件处理器指向事件对于的业务逻辑。
- 通常包含一个或多个handle_event回调函数,这些函数在事件循环中被执行。
- 用户需要继承IO框架库提供的事件处理器,来实现自己的事件处理器,也就是
具体事件处理器
- IO框架库中的事件处理器会被声明为虚函数。
- get_handler方法:返回与该事件相关联的句柄。当事件发生时,通过句柄来通知应用程序。
-
Reactor
- Reactor是IO框架库核心:
- handle_events方法:执行事件循环。重复如下过程:等待事件,依次处理所有就绪事件对应的事件处理器。
- rigister_handler:调用事件多路分发器的register_event方法来往事件多路分发器注册事件。
- remove_handler:调用事件多路分发器的remove_event方法来删除事件多路分发器注册事件。
- Reactor是IO框架库核心:
-
12.2 Libevent源码分析
- Linux下Libevent的安装
【Linux】Linux系统下libevent库的安装 - Libevent的简单案例:
#include <sys/signal.h>
#include <event.h>
void signal_cb(int, short, void*);
void timeout_cb(int, short, void*);
int main(){
//创建event_base对象,一个event_base相当于一个Reactor实例
struct event_base* base = event_init();
//创建具体的事件处理器,并设置他们从属的Reactor实例
struct event* signal_event = evsignal_new(base, SIGINT, signal_cb, base);
//把事件添加到注册事件队列,并把事件处理器对应的事件添加到时间多路分发器。
event_add(signal_event, NULL);
timeval tv = {1, 0};
struct event* timeout_event = evtimer_new(base, timeout_cb, NULL);
event_add(timeout_event, &tv);
//执行事件循环
event_base_dispatch(base);
//释放系统资源
event_free(timeout_event);
event_free(signal_event);
event_base_free(base);
return 0;
}
void signal_cb(int fd, short event, void* argc){
struct event_base* base = (event_base* )argc;
struct timeval delay = {2, 0};
printf("Caught an interrupt signal, exiting cleanly in two seconds...\n");
event_base_loopexit(base, &delay);
}
void timeout_cb(int fd, short event, void* argc){
printf("timeout\n");
}
函数解析:
-
1.event_init函数:
struct event_base* base = event_init();
- 创建event_base对象,一个event_base相当于一个Reactor实例。
-
2.evsignal_event函数、evtimer_new函数,分别用于创建信号事件处理器,定时事件处理器。
#define evsignal_new(b, x, cb, arg) event_new((b), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg))
#define evtimer_new(b, cb, arg) event_new((b), -1, 0, (cb), (arg))
//event_new函数的定义:
struct event* event_new(struct event_base* base, evutil_socket_t fd, short events, void (*cb)(evutil_socket_t, short, void*), void* arg);
-
对于event_new函数,其参数含义如下:
- base:指定新创建的事件处理器的从属Reactor。
- fd:指定与该事件处理器关联的句柄。对于IO事件处理器,fd是文件描述符;对于信号事件处理器,fd是信号值;对于定时事件处理器,要给fd传递-1。
- events:指定事件类型
EV_PERSIST作用:事件被触发后,自动对这个event调用event_add函数。 - cb:指定目标事件的回调函数
- arg:回调函数的参数
-
3.event_add函数:将事件处理器添加到注册事件队列。并将该事件处理器对于的事件添加到事件多路分发器。
-
4.event_base_dispatch函数执行事件循环
-
5.*free系列函数用于释放系统资源
-
6. event_base_loopexit退出事件循环。
int event_base_loopexit(event_base *, const timeval *)
- event_base指定要退出的事件循环
- timeval指定多久之后退出
12.2.2 源代码组织结构
略
12.2.3 event结构体
- event结构体封装了句柄、事件类型、回调函数、以及其他必要的标志和数据
struct event {
struct event_callback ev_evcallback;
/* for managing timeouts */
union {
TAILQ_ENTRY(event) ev_next_with_common_timeout;
int min_heap_idx;
} ev_timeout_pos;
evutil_socket_t ev_fd;
struct event_base *ev_base;
union {
/* used for io events */
struct {
LIST_ENTRY (event) ev_io_next;
struct timeval ev_timeout;
} ev_io;
/* used by signal events */
struct {
LIST_ENTRY (event) ev_signal_next;
short ev_ncalls;
/* Allows deletes in callback */
short *ev_pncalls;
} ev_signal;
} ev_;
short ev_events; //事件类型
short ev_res; /* result passed to event callback */
struct timeval ev_timeout;
};
略
12.2.4 往注册事件队列中添加事件处理器
略