一 本次实例使用函数简介
事件集合初始化:
struct event_base *event_init(void);
示例:
struct event_base *base = event_init();
单个事件初始化
void event_set(struct event *ev, evutil_socket_t fd, short events,void (*callback)(evutil_socket_t, short, void *), void *arg);
evutil_socket_t 的定义,在linux环境就是int类型
#ifdef _WIN32
#define evutil_socket_t intptr_t
#else
#define evutil_socket_t int
#endif
事件类型
/*** @name event flags** Flags to pass to event_new(), event_assign(), event_pending(), and* anything else with an argument of the form "short events"*/
/**@{*/
/** Indicates that a timeout has occurred. It's not necessary to pass* this flag to event_for new()/event_assign() to get a timeout. */
#define EV_TIMEOUT 0x01
/** Wait for a socket or FD to become readable */
#define EV_READ 0x02
/** Wait for a socket or FD to become writeable */
#define EV_WRITE 0x04
/** Wait for a POSIX signal to be raised*/
#define EV_SIGNAL 0x08
/*** Persistent event: won't get removed automatically when activated.** When a persistent event with a timeout becomes activated, its timeout* is reset to 0.*/
#define EV_PERSIST 0x10
/** Select edge-triggered behavior, if supported by the backend. */
#define EV_ET 0x20
/*** If this option is provided, then event_del() will not block in one thread* while waiting for the event callback to complete in another thread.** To use this option safely, you may need to use event_finalize() or* event_free_finalize() in order to safely tear down an event in a* multithreaded application. See those functions for more information.**/
#define EV_FINALIZE 0x40
/*** Detects connection close events. You can use this to detect when a* connection has been closed, without having to read all the pending data* from a connection.** Not all backends support EV_CLOSED. To detect or require it, use the* feature flag EV_FEATURE_EARLY_CLOSE.**/
#define EV_CLOSED 0x80
创建事件示例:
void read_callback(evutil_socket_t fd, short events, void *arg){//process}struct event ev;event_set(&ev,fd,EV_READ | EV_PERSIST,read_callback,NULL);
添加事件到集合:
int event_add(struct event *ev, const struct timeval *tv)
示例:
event_add(&ev,NULL);
引出current_base
event_add这里为什么没有调用struct event_base* base呢?看下event_add的实现
int
event_add(struct event *ev, const struct timeval *tv)
{int res;if (EVUTIL_FAILURE_CHECK(!ev->ev_base)) {event_warnx("%s: event has no event_base set.", __func__);return -1;}EVBASE_ACQUIRE_LOCK(ev->ev_base, th_base_lock);res = event_add_nolock_(ev, tv, 0);EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock);return (res);
}
这里用到了一个成员ev->ev_base,它的定义是struct event_base *ev_base;它是在哪里初始化的呢,应该是在event_set里,event_set的函数原型如下所示
void
event_set(struct event *ev, evutil_socket_t fd, short events,void (*callback)(evutil_socket_t, short, void *), void *arg)
{int r;r = event_assign(ev, current_base, fd, events, callback, arg);EVUTIL_ASSERT(r == 0);
}
这个函数用到了current_base,这是一个全局变量,定义如下:
struct event_base *event_global_current_base_ = NULL;
#define current_base event_global_current_base_
它是在event_init中初始化的。
struct event_base *
event_init(void)
{struct event_base *base = event_base_new_with_config(NULL);if (base == NULL) {event_errx(1, "%s: Unable to construct event_base", __func__);return NULL;}current_base = base;return (base);
}
从这个函数的定义可知,一个程序只能定义一个struct event_base。因为每次初始化都会给current_base 赋值,所以,我们完全没有必要保存event_init的返回值吗?是这样理解的吗?至少使用当前这几个函数,是这样的。
开始监听:
event_dispatch();
开始监听,死循环,如果集合中没有事件可以监听,则返回。
int
event_dispatch(void)
{return (event_loop(0));
}
测试代码
#include <sys/types.h>
#include <event2/event-config.h>
#include <stdio.h>
#include <event.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>#define _DEBUG_INFO
#ifdef _DEBUG_INFO
#define DEBUG_INFO(format, ...) printf("%s:%d $$ " format "\n" \
,__func__,__LINE__ \
, ##__VA_ARGS__)
#else
#define DEBUG_INFO(format, ...)
#endif
// event_init
//event_set
//event_get
#ifdef _WIN32
#define evutil_socket_t intptr_t
#else
#define evutil_socket_t int
#endif
void event_set(struct event *ev, evutil_socket_t fd, short events,void (*callback)(evutil_socket_t, short, void *), void *arg);void fifo_read_callback(evutil_socket_t fd, short events, void *arg)
{struct event *ev = arg;DEBUG_INFO("%s %s %s ",((events & EV_READ)?"EV_READ":""),((events & EV_PERSIST)?"EV_PERSIST":""),((events & EV_CLOSED)?"EV_CLOSED":""));DEBUG_INFO("read fd = %d events = %d\n", fd, events);char buf[100 + 1];memset(buf, 0, sizeof(buf));int ret = read(fd, buf, sizeof(buf) - 1);if(-1 == ret) {perror("read");exit(-1);}if(ret == 0){if(dup2(fd,fd) < 0){perror("dup2");DEBUG_INFO("fd = %d 已关闭");return;}else{DEBUG_INFO("fd = %d 还没有被关闭");}event_del(ev);close(fd);DEBUG_INFO("fd = %d 已关闭");return ;}DEBUG_INFO("ret = %d,buf = %s\n",ret, buf);
}
char *fifo_name;
int main(int argc, char **argv)
{if(argc < 2){fifo_name = "fifo.tmp";}else{fifo_name = argv[1];}int ret = mkfifo(fifo_name,0666);if(ret == -1){if(errno == EEXIST){DEBUG_INFO("%s exist",fifo_name);}else{perror("mkfifo");exit(-1);}}DEBUG_INFO("create %s ok",fifo_name);int fd = open(fifo_name,O_RDONLY);if(fd == -1){perror("open");exit(-1);}//初始化事件集合struct event_base *base;base = event_init();//初始化事件struct event ev;event_set(&ev,fd,EV_READ | EV_PERSIST ,fifo_read_callback,&ev);//把事件添加到集合中event_add(&ev,NULL);//开始监听,死循环,如果集合中没有事件可以监听,则返回。event_dispatch();DEBUG_INFO("byebye");return 0;
}
测试结果:
在测试用使用 cat > fifo.tmp将终端输入的数据重定向到管道中,如下所示,不是cat fifo.tmp,如果写成cat fifo.tmp那就是读管道了。如果两个程序同时读管道,那两个程序就阻塞了。
开始测试,打开两个终端,左边运行测试代码,右边运行cat > fifo.tmp,向管道中写数据,如下所示:
实验解析:
集合创建和事件添加前面都有说过,程序运行后,如果还没有运行写管道程序,会卡在open的地方,如果运行了cat > fifo.tmp。会继续向下执行到event_dispatch这里,只要集合base中有在监听,event_dispatch函数就不会返回,此时,在右边的终端中输入hello fifo,并回车,左边读到数据。在fifo_read_callback函数中,当右边的终端关闭管道时,也就是按下CTRL+C,read返回0,此时需调用event_del将事件从集合中删除。
int
event_del(struct event *ev)
{return event_del_(ev, EVENT_DEL_AUTOBLOCK);
}
该函数中调用了event_del_,在event_del_函数中存在加锁EVBASE_ACQUIRE_LOCK和解锁EVBASE_RELEASE_LOCK,由此可见,这个函数是线程安全的。不需要做额外的线程安全处理。
static int
event_del_(struct event *ev, int blocking)
{int res;struct event_base *base = ev->ev_base;if (EVUTIL_FAILURE_CHECK(!base)) {event_warnx("%s: event has no event_base set.", __func__);return -1;}EVBASE_ACQUIRE_LOCK(base, th_base_lock);res = event_del_nolock_(ev, blocking);EVBASE_RELEASE_LOCK(base, th_base_lock);return (res);
}