(3)libevent 事件
事件event
libevent 的基本操作单元是事件。每个事件代表一组条件的集合,这些条件包括:
- 文件描述符已经就绪,可以读取或者写入
- 文件描述符变为就绪状态,可以读取或者写入(仅对于边沿触发 IO)
- 超时事件
- 发生某信号
- 用户触发事件
1.创建事件
回调函数:
typedef void (*event_callback_fn)(evutil_socket_t, short, void *);
创建事件:
struct event *event_new(
struct event_base *base,
evutil_socket_t fd,
short what,
event_callback_fn cb,
void *arg);
释放事件:
void event_free(struct event *event);
2.事件标志
#define EV_TIMEOUT 0x01
这个标志表示某超时时间流逝后事件成为激活的。构造事件的时候,EV_TIMEOUT 标志是 被忽略的:可以在添加事件的时候设置超时 ,也可以不设置。超时发生时,回调函数的 what 参数将带有这个标志。
#define EV_READ 0x02
表示指定的文件描述符已经就绪,可以读取的时候,事件将成为激活的
#define EV_WRITE 0x04
表示指定的文件描述符已经就绪,可以写入的时候,事件将成为激活的。
#define EV_SIGNAL 0x08
用于实现信号检测
#define EV_PERSIST 0x10EV_PERSIST
表示事件是“持久的”
#define EV_ET 0x20
表示底层的 event_base 后端支持边沿触发事件,则事件应该是边沿触发的,这个标志 影响 EV_READ 和 EV_WRITE 的语义。
3.事件持久性
-
默认情况下,每当未决事件成为激活的,事件将在其回调被执行前成为非未决的。如果想让事件再次成为未决的 ,可以在回调函数中 再次对其调用 event_add()。
-
设置了 EV_PERSIST 标志,事件就是持久的。事件还是会保持为未决状态。如果想在回调中让事件成为非未决的 ,可以对其调用 event_del ()。
-
每次执行事件回调的时候,持久事件的超时值会被复位。如果具有 EV_READ|EV_PERSIST 标志,以及5秒的超时值,则事件将在以下情况下成为激活的(1套接字已经准备好被读取的时候; 2从最后一次成为激活的开始,已经逝去 5秒)
void cb_func(evutil_socket_t fd, short what, void *arg)
{
const char *data = arg;
printf("Got an event on socket %d:%s%s%s%s [%s]",
(int) fd,
(what&EV_TIMEOUT) ? " timeout" : "",
(what&EV_READ) ? " read" : "",
(what&EV_WRITE) ? " write" : "",
(what&EV_SIGNAL) ? " signal" : "",
data);
}
void main_loop(evutil_socket_t fd1, evutil_socket_t fd2)
{
struct event *ev1, *ev2;
struct timeval five_seconds = {5,0};
struct event_base *base = event_base_new();
ev1 = event_new(base, fd1, EV_TIMEOUT|EV_READ|EV_PERSIST, cb_func,
(char*)"Reading event");
ev2 = event_new(base, fd2, EV_WRITE|EV_PERSIST, cb_func,
(char*)"Writing event");
event_add(ev1, &five_seconds);
event_add(ev2, NULL);
event_base_dispatch(base);
}
4.添加/删除 事件
int event_add(struct event *ev, const struct timeval *tv);
int event_del(struct event *ev);
5.事件的优先级
- libevent 会执行高优先级的事件,然后重新检查各个事件。只有在没有高优先级的事件是激活的时候 ,低优先级的事件才会运行。
- 优先级将从 0 (最高) 到 n-1(最低)
int event_priority_set(struct event *event, int priority);
void read_cb(evutil_socket_t, short, void *);
void write_cb(evutil_socket_t, short, void *);
void main_loop(evutil_socket_t fd)
{
struct event *important, *unimportant;
struct event_base *base;
base = event_base_new();
event_base_priority_init(base, 2);
important = event_new(base, fd, EV_WRITE|EV_PERSIST, write_cb, NULL);
unimportant = event_new(base, fd, EV_READ|EV_PERSIST, read_cb, NULL);
event_priority_set(important, 0);
event_priority_set(unimportant, 1);
}
6.检查事件状态
/*事件是否是未执行或者激活的*/
int event_pending(const struct event *ev, short what, struct timeval *tv_out);
/*返回事件配置的文件描述符或者信号值*/
evutil_socket_t event_get_fd(const struct event *ev);
/*获取事件的event_base*/
struct event_base *event_get_base(const struct event *ev);
/*获取事件的状态*/
short event_get_events(const struct event *ev);
/*获取事件的回调函数*/
event_callback_fn event_get_callback(const struct event *ev);
/*获取事件的回调参数*/
void *event_get_callback_arg(const struct event *ev);
/*获取事件的优先级别*/
int event_get_priority(const struct event *ev);
/*复制所有为事件分配的字段到提供的指针中。任何为 NULL 的参数会被忽略*/
void event_get_assignment(const struct event *event,
struct event_base **base_out,
evutil_socket_t *fd_out,
short *events_out,
event_callback_fn *callback_out,
void **arg_out);
8.一次触发事件
- 除了不支持 EV_SIGNAL 或者 EV_PERSIST 之外,这个函数的接口与 event_new()相同。
- 不能删除或者手动激活使用 event_base_once ()插入的事件:如果希望能够取消事件, 应该使用 event_new()重新赋值
int event_base_once(struct event_base *, evutil_socket_t, short, void (*)(evutil_socket_t, short, void *), void *, const struct timeval *);
9.手动激活事件
- 极少数情况下,需要在事件的条件没有触发的时候让事件成为激活的。
void event_active(struct event *ev, int what, short ncalls);
10.信号事件:提供一个信号编号代替文件描述符之外,各个参数与 event_new()相同。
- 信号回调是信号发生后在事件循环中被执行的,所以可以安全地调用通常不能 在 POSIX 风格信号处理器中使用的函数。
#define evsignal_new(base, signum, cb, arg) event_new(base, signum, EV_SIGNAL|EV_PERSIST, cb, arg)
#define evsignal_add(ev, tv) event_add((ev),(tv))
#define evsignal_del(ev) event_del(ev)
#define evsignal_pending(ev, what, tv_out) event_pending((ev), (what), (tv_out))
struct event_base *base = event_base_new();
struct event *hup_event = evsignal_new(base, SIGHUP, sighup_function, NULL);
一、libevent监听日志
#include <iostream>
#include <event2/event.h>
#ifndef _WIN32
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#else
#endif
#include <thread>
using namespace std;
void read_file(evutil_socket_t fd,short event, void *arg)
{
char buf[1024] = {0};
int len = read(fd,buf,sizeof(buf)-1);
if(len>0)
{
cout<<buf<<endl;
}
else
{
cout<<"."<<flush;
this_thread::sleep_for(500ms);
}
}
int main(int argc,char *argv[])
{
event_config *conf = event_config_new();
//设置支持文件描述符EV_FEATURE_FDS模式
event_config_require_features(conf,EV_FEATURE_FDS);
event_base *base = event_base_new_with_config(conf);
event_config_free(conf);
if(!base)
{
cerr<<"event_base_new_with_config failed!"<<endl;
return -1;
}
//打开文件只读,非阻塞
int sock = open("/var/log/auth.log",O_RDONLY|O_NONBLOCK,0);
if(sock<=0)
{
cerr<<"open /var/log/auth.log failed!"<<endl;
return -2;
}
//文件指针移到结尾处
lseek(sock,0,SEEK_END);
//监听文件数据
event *fev = event_new(base,sock,EV_READ|EV_PERSIST,read_file,0);
event_add(fev,NULL);
//进入事件主循环
event_base_dispatch(base);
event_base_free(base);
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步