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


















