一 本次实例使用函数简介
事件集合初始化:
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);
}