函数声明 | 功能 |
---|---|
const char **event_get_supported_methods(void); | 返回一个指针 ,指向 libevent 支持的IO多路方法名字数组,这个数组的最后一个元素是NULL |
const char *event_base_get_method(const struct event_base *base); | 返回 event_base 正在使用的IO多路方法 |
enum event_method_feature event_base_get_features(const struct event_base *base); | 返回 event_base 支持的特征的比特掩码 |
//libevent的版本
printf("Starting Libevent %s. Available methods are:\n", event_get_version());
//检查支持的IO多路方法
const char **methods = event_get_supported_methods();
for (int i=0; methods[i] != NULL; ++i) {
printf(" %s\n", methods[i]);
}
struct event_base *base = event_base_new();
enum event_method_feature f;
if (!base)
{
puts("Couldn't get an event_base!");
}
else
{
//返回 event_base 正在使用的IO多路方法
printf("Using Libevent with backend method %s\n",event_base_get_method(base));
//返回 event_base 支持的特征的比特掩码
f = event_base_get_features(base);
if ((f & EV_FEATURE_ET)) //支持边沿触发的后端
printf(" Edge-triggered events are supported.");
if ((f & EV_FEATURE_O1)) //添加、删除单个事件,或者确定哪个事件激活的操作是 O(1)复杂度的后端
printf(" O(1) event notification is supported.");
if ((f & EV_FEATURE_FDS)) //要求支持任意文件描述符,而不仅仅是套接字的后端
printf(" All FD types are supported.");
}
struct event_base *event_base_new(void);
功能:函数会检测环境变量,返回一个event_base的指针,分配并且返回一个新的具有默认设置的 event_base。
void event_base_free(struct event_base *base); //释放event_base
注意:这个函数不会释放当前与 event_base 关联的任何事件,或者关闭它们的套接字 ,或者释放任何指针。应该手动的释放它们
int event_reinit(struct event_base *base);
因为不是所有的安插在event_base的事件在调用fork()之后都可以正常工作,所以,如果在使用fork()或者其他相关系统调用启动一个新的进程之后,要想在子进程中使用base变量,但是又想让该base变量是一个全新的没有安插事件的变量,就应该在子进程中对base调用event_reinit函数进行重新初始化。
[伪代码]
struct event_base* base=event_base_new();
//向event_base中安插事件
if(fork()) // parent process
{
continue_runing_parent(base);
}
else //child process
{
event_reinit(base); //重新初始化子进程从父进程继承下来的base
continue_runing_child(base);
}
int event_base_priority_init(struct event_base *base, int n_priorities);
默认情况下,与 event_base 相关联的事件的优先级将默认被初始化为 n_priorities / 2
一旦创建好事件根基event_base,并且在根基上安插好事件之后,需要对事件循环监控(换句话说就是等待事件的到来,触发事件的回调函数),有两种方式可以达到上面描述的功能,即:event_base_dispatch和event_base_loop
event_base_dispatch函数退出的三种情况 |
---|
当所有的事件都active完毕,此时没有处于未决状态(正在被监听)的事件时 |
调用event_base_loopexit(struct event_base*, const struct timeval*)函数 |
调用event_base_loopbreak(struct event_base*)函数 |
如果想在移除所有已注册的事件之前停止活动的事件循环,可以调用两个稍有不同的函数
函数声明 | 功能与区别 |
---|---|
int event_base_loopexit(struct event_base *base, const struct timeval *tv); | 让 event_base 在给定时间之后停止循环。如果 tv 参数为 NULL ,event_base 会立即停止循环,没有延时。如果 event_base 当前正在执行任何激活事件的回调,则回调会继续运行,直到运行完所有激活事件的回调之才退出 |
int event_base_loopbreak(struct event_base *base); | 让 event_base 立即退出循环(即使有其他正在执行任何激活事件的回调) |
void event_base_dump_events(struct event_base *base, FILE *f);
功能:将event_base上安插的事件情况,写入到绑定的文件中
FILE *fp = fopen("event_base_stat.txt","a");
event_base_dump_events(base, fp); //将base的状态,保存到文件中
fclose(fp);
在base上安装、监控event的流程 |
---|
struct event* ev=event_new(base,fd,what,cb,arg)创建事件,事件处于非未决/初始化状态 |
event_add(ev,NULL); //使事件处于未决状态 |
event_base_dispatch(base); //循环检测事件,事件发生时触发回调函数 |
event_free(ev);释放事件 |
event_base_free(base);释放event_base |
信号事件signalEvent、事件event的使用步骤对比
可以看到:
警告:不要在信号事件上设置超时,这可能是不被支持的。[待修正:真是这样的吗?]
信号事件signalEvent | 事件event |
---|---|
struct event *signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base); | struct event* ev=event_new(base,fd,what,cb,arg) |
event_add(signal_event, NULL) | event_add(ev,NULL); |
创建并初始化struct event类型的事件变量,根据创建的位置分为堆/栈
创建位置 | 函数原型 |
---|---|
堆区 | struct event *event_new(struct event_base *base, evutil_socket_t fd, short what, void (*cb)(evutil_socket_t, short, void *), void *arg); |
栈区 | int event_assign(struct event *ev, struct event_base *base, evutil_socket_t fd, short events, void (*callback)(evutil_socket_t, short, void *), void *arg) |
event_new | event_assign | |
---|---|---|
功能 | 试图在堆上分配和构造一个用于 base 的新的event | 在栈上已经初始化好struct event类型的变量,使用event_assign函数对其进行初始化 |
使用步骤 | struct event * ev; | struct event ev; |
ev=event_new(_,_,_,_,__); | event_assign(&ev,_,_,_,_,__); | |
警告 | 不要对已经在 event_base 中处于未决状态的事件调用 event_assign(如果已经初始化和成为未决的,需要调用event_assign之前需要调用event_del) |
参数介绍:
[重点]
关于事件持久性:EV_PERSIST
(1)是否设置EV_PERSIST标志的本质区别:事件从active状态结束后,将变成 [ 未决状态 / 非未决状态 ] ?
是否设置EV_PERSIST持久标志? | 事件在进入active状态并且回调函数执行完毕后,事件状态从active状态变成什么状态? |
---|---|
设置 | 未决状态(事件仍然被监听) |
未设置 | 非未决状态(事件已经不被监听) |
(2)分析程序,深刻的体会设置/不设置EV_PERSIST的区别
输入任意字符,按回车后,代码的执行结果 | |
---|---|
情况1:不设置EV_PERSIST持久标志 | 事件被触发后,事件状态进入非未决状态,此时没有被监听的事件 ==> 导致程序执行callback后,直接退出 |
情况2:设置EV_PERSIST持久标志 | 事件被触发后,事件状态进入未决状态,此时事件仍然被监听;并且缓冲区的数据一直存在 ==> 导致程序一直执行callback函数,不断的打印横线 |
void event_cb(evutil_socket_t fd, short what, void *arg)
{
printf("____________________\n");
}
int main()
{
struct event_base* base = event_base_new();
int fd = 0;
//情况1:不设置EV_PERSIST持久标志
struct event* ev = event_new(base,fd,EV_TIMEOUT|EV_READ/*|EV_PERSIST*/,event_cb,NULL);
//情况2:设置EV_PERSIST持久标志
struct event* ev = event_new(base,fd,EV_TIMEOUT|EV_READ|EV_PERSIST,event_cb,NULL);
event_add(ev,NULL); //使事件处于未决状态
event_base_dispatch(base); //循环检测事件,直到没有要注册的事件或者调用exit/break函数
event_free(ev); // 释放事件 void event_free(struct event *event);
event_base_free(base);
return 0;
}
功能:让事件从非未决状态/未决状态之间切换
int event_add(struct event *ev, const struct timeval *tv);
int event_del(struct event *ev);
int event_priority_set(struct event *event, int priority); //设置事件event的优先级priority,其中priority属于[0, n_priorities-1]
设置事件优先级的步骤 | 相关API使用 |
---|---|
1. 创建event_base和event | event_base_new()、event_new() |
2. 设置event_base支持的[优先级的数目n_priorities] | event_base_priority_init(base, n_priorities); |
3. 设置event的优先级 | event_priority_set(ev, 0); |
4. 将event安插到event_base上 |
说明:如果不为事件event设置优先级,则默认的优先级等于 event_base 的优先级数目n_priorities除以2
void event_active(struct event *ev, int what, short ncalls);
功能:使没被触发的事件,成为active状态
重点:
代码案例:
程序执行结果:程序执行一次event_cb函数后,直接退出
分析原因:调用event_active函数后,事件ev并没有变成“未决状态”,导致此时没有待监控的事件,所以event_base_dispatch函数返回,程序退出
void event_cb(evutil_socket_t fd, short what, void *arg)
{
printf("____________________\n");
}
int main()
{
struct event_base* base = event_base_new();
int fd = 0;
//EV_PERSIST永久监控EV_READ事件
struct event* ev = event_new(base,fd,EV_TIMEOUT|EV_READ|EV_PERSIST,event_cb,NULL);
//event_add(ev,NULL); //不需要通过event_add使事件ev处于未决状态
event_active(ev,EV_READ,0); //触发事件后,事件并没进入未决状态
event_base_dispatch(base);
event_free(ev);
event_base_free(base);
return 0;
}
函数原型:int event_base_once(struct event_base *, evutil_socket_t, short, void (*)(evutil_socket_t, short, void *), void *, const struct timeval *);