Libevent学习日记(二)

Ubuntu下载Libevent

wget https://github.com/libevent/libevent/releases/download/release-2.1.12-stable/libevent-2.1.12-stable.tar.gz
  • 使用tar指令解压
tar -zxvf libevent-2.1.12-stable.tar.gz
  • ./configure 进行配置
    如果出现这种情况,按它说的加上 --disable-openssl就行。

configure: error: openssl is a must but can not be found. You should add the directory containing ‘openssl.pc’ to the ‘PKG_CONFIG_PATH’ environment variable, or set ‘CFLAGS’ and ‘LDFLAGS’ directly for openssl, or use `–disable-openssl’ to disable support for openssl encryption

  • make 编译
  • sudo make install 下载到系统lib
  • 安装完后查看本地的库文件
    在这里插入图片描述
  • 使用方法:
    在g++后添加 -levent选项。
  • libevent使用步骤

(1)创建socket
(2)创建事件集event_base
(3)创建event_new(注册回调函数)
(4)添加事件到事件集event_base
(5)进行事件检测,event_base_dispatch()/event_base_loop()

注意:libevent并没有对socket进行封装,而是对epoll、select等进行封装,所以在程序中我们还要自己建立socket。

event_base

使用 libevent 函数之前需要分配一个或者多个 event_base 结构体。每个 event_base 结构 体持有一个事件集合,可以检测以确定哪个事件是激活的。
如果设置 event_base 使用锁,则可以安全地在多个线程中访问它 。然而,其事件循环只能 运行在一个线程中。如果需要用多个线程检测 IO,则需要为每个线程使用一个 event_base。

每个 event_base 都有一种用于检测哪种事件已经就绪的 “方法”,或者说后端。可以识别的方法有:

  • select
  • poll
  • epoll
  • kqueue
  • devpoll
  • evport
  • win32

创建默认的event_base

event_base_new()函数分配并且返回一个新的具有默认设置的 event_base。函数会检测环境变量,返回一个到 event_base 的指针。如果发生错误,则返回 NULL。选择各种方法时,函数会选择 OS 支持的最快方法。

struct event_base *event_base_new(void);

下面是event_base结构的源码,每个成员都有很详细的介绍,以后用到什么再看什么。

struct event_base {
    
    
	/** Function pointers and other data to describe this event_base's
	 * backend. */
	const struct eventop *evsel;
	/** Pointer to backend-specific data. */
	void *evbase;

	/** List of changes to tell backend about at next dispatch.  Only used
	 * by the O(1) backends. */
	struct event_changelist changelist;

	/** Function pointers used to describe the backend that this event_base
	 * uses for signals */
	const struct eventop *evsigsel;
	/** Data to implement the common signal handler code. */
	struct evsig_info sig;

	/** Number of virtual events */
	int virtual_event_count;
	/** Maximum number of virtual events active */
	int virtual_event_count_max;
	/** Number of total events added to this event_base */
	int event_count;
	/** Maximum number of total events added to this event_base */
	int event_count_max;
	/** Number of total events active in this event_base */
	int event_count_active;
	/** Maximum number of total events active in this event_base */
	int event_count_active_max;

	/** Set if we should terminate the loop once we're done processing
	 * events. */
	int event_gotterm;
	/** Set if we should terminate the loop immediately */
	int event_break;
	/** Set if we should start a new instance of the loop immediately. */
	int event_continue;

	/** The currently running priority of events */
	int event_running_priority;

	/** Set if we're running the event_base_loop function, to prevent
	 * reentrant invocation. */
	int running_loop;

	/** Set to the number of deferred_cbs we've made 'active' in the
	 * loop.  This is a hack to prevent starvation; it would be smarter
	 * to just use event_config_set_max_dispatch_interval's max_callbacks
	 * feature */
	int n_deferreds_queued;

	/* Active event management. */
	/** An array of nactivequeues queues for active event_callbacks (ones
	 * that have triggered, and whose callbacks need to be called).  Low
	 * priority numbers are more important, and stall higher ones.
	 */
	struct evcallback_list *activequeues;
	/** The length of the activequeues array */
	int nactivequeues;
	/** A list of event_callbacks that should become active the next time
	 * we process events, but not this time. */
	struct evcallback_list active_later_queue;

	/* common timeout logic */

	/** An array of common_timeout_list* for all of the common timeout
	 * values we know. */
	struct common_timeout_list **common_timeout_queues;
	/** The number of entries used in common_timeout_queues */
	int n_common_timeouts;
	/** The total size of common_timeout_queues. */
	int n_common_timeouts_allocated;

	/** Mapping from file descriptors to enabled (added) events */
	struct event_io_map io;

	/** Mapping from signal numbers to enabled (added) events. */
	struct event_signal_map sigmap;

	/** Priority queue of events with timeouts. */
	struct min_heap timeheap;

	/** Stored timeval: used to avoid calling gettimeofday/clock_gettime
	 * too often. */
	struct timeval tv_cache;

	struct evutil_monotonic_timer monotonic_timer;

	/** Difference between internal time (maybe from clock_gettime) and
	 * gettimeofday. */
	struct timeval tv_clock_diff;
	/** Second in which we last updated tv_clock_diff, in monotonic time. */
	time_t last_updated_clock_diff;

#ifndef EVENT__DISABLE_THREAD_SUPPORT
	/* threading support */
	/** The thread currently running the event_loop for this base */
	unsigned long th_owner_id;
	/** A lock to prevent conflicting accesses to this event_base */
	void *th_base_lock;
	/** A condition that gets signalled when we're done processing an
	 * event with waiters on it. */
	void *current_event_cond;
	/** Number of threads blocking on current_event_cond. */
	int current_event_waiters;
#endif
	/** The event whose callback is executing right now */
	struct event_callback *current_event;

#ifdef _WIN32
	/** IOCP support structure, if IOCP is enabled. */
	struct event_iocp_port *iocp;
#endif

	/** Flags that this base was configured with */
	enum event_base_config_flag flags;

	struct timeval max_dispatch_time;
	int max_dispatch_callbacks;
	int limit_callbacks_after_prio;

	/* Notify main thread to wake up break, etc. */
	/** True if the base already has a pending notify, and we don't need
	 * to add any more. */
	int is_notify_pending;
	/** A socketpair used by some th_notify functions to wake up the main
	 * thread. */
	evutil_socket_t th_notify_fd[2];
	/** An event used by some th_notify functions to wake up the main
	 * thread. */
	struct event th_notify;
	/** A function used to wake up the main thread from another thread. */
	int (*th_notify_fn)(struct event_base *base);

	/** Saved seed for weak random number generator. Some backends use
	 * this to produce fairness among sockets. Protected by th_base_lock. */
	struct evutil_weakrand_state weakrand_seed;

	/** List of event_onces that have not yet fired. */
	LIST_HEAD(once_event_list, event_once) once_events;

};

创建复杂的event_base

要创建非默认的event_base,就需要用到event_config。

event_config 是一个容纳 event_base 配置信息的不透明结构体。需要 event_base 时,将event_config 传递给event_base_new_with_config ()。

接口

struct event_config *event_config_new(void);
struct event_base *
event_base_new_with_config(const struct event_config *cfg);
void event_config_free(struct event_config *cfg);

使用步骤

  • 调用event_config_new() 分配一个event_config。
  • event_config调用其它函数设置event_base特征。
  • 最后调用event_base_new_with_config()获取新的event_base。
  • 完成工作后,使用 event_config_free ()释放 event_config。

后端设置与特征选择

调用 event_config_avoid_method ()可以通过名字让 libevent 避免使用特定的可用后端 。 调用 event_config_require_feature ()让 libevent 不使用不能提供所有指定特征的后端。 调用 event_config_set_flag()让 libevent 在创建 event_base 时设置一个或者多个将在下面介绍的运行时标志。

int event_config_avoid_method(struct event_config *cfg, const char *method);
enum event_method_feature {
    
    
    EV_FEATURE_ET = 0x01,
    EV_FEATURE_O1 = 0x02,
    EV_FEATURE_FDS = 0x04,
};
int event_config_require_features(struct event_config *cfg,
                                  enum event_method_feature feature);
enum event_base_config_flag {
    
    
    EVENT_BASE_FLAG_NOLOCK = 0x01,
    EVENT_BASE_FLAG_IGNORE_ENV = 0x02,
    EVENT_BASE_FLAG_STARTUP_IOCP = 0x04,
    EVENT_BASE_FLAG_NO_CACHE_TIME = 0x08,
    EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST = 0x10,
    EVENT_BASE_FLAG_PRECISE_TIMER = 0x20
};
int event_config_set_flag(struct event_config *cfg,
    enum event_base_config_flag flag);

event_config_require_features()可识别的特征值有:

  • EV_FEATURE_ET:要求支持边沿触发的后端
  • EV_FEATURE_O1:要求添加、删除单个事件,或者确定哪个事件激活的操作是 O(1)复杂度的后端
  • EV_FEATURE_FDS:要求支持任意文件描述符,而不仅仅是套接字的后端

event_config_set_flag()可识别的选项值有:

  • EVENT_BASE_FLAG_NOLOCK :不要为 event_base 分配锁。设置这个选项可以 为 event_base 节省一点用于锁定和解锁的时间,但是让在多个线程中访问 event_base 成为不安全的。

  • EVENTBASE_FLAG_IGNORE_ENV :选择使用的后端时,不要检测 EVENT* 环境 变量。使用这个标志需要三思:这会让用户更难调试你的程序与 libevent 的交互。

  • EVENT_BASE_FLAG_STARTUP_IOCP:仅用于 Windows,让 libevent 在启动时就启用任何必需的 IOCP 分发逻辑,而不是按需启用。

  • EVENT_BASE_FLAG_NO_CACHE_TIME :不是在事件循环每次准备执行超时回调时检测当前时间,而是在每次超时回调后进行检测。注意:这会消耗更多的 CPU时间。

  • EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST :告诉 libevent ,如果决定使 用 epoll 后端,可以安全地使用更快的基于 changelist 的后端。epoll-changelist 后端可以在后端的分发函数调用之间,同样的 fd 多次修改其状态的情况下,避免不必要的系统 调用。但是如果传递任何使用 dup()或者其变体克隆的 fd 给 libevent,epoll-changelist 后端会触发一个内核 bug,导致不正确的结果。在不使用 epoll 后端的情况下,这个标志是没有效果的。也可以通过设置 EVENT_EPOLL_USE_CHANGELIST 环境变量来打开 epoll-changelist 选项。
    上述操作 event_config 的函数都在成功时返回0,失败时返回-1。

检查event_base后端

目的:查看当前event_base支持哪些特征和方法。

接口一

const char **event_get_supported_methods(void);

根据名字就能判断,获得了 它支持的方法名字数组,最后一个元素是NULL。

demo

int i;
const char **methods = event_get_supported_methods();
printf("Starting Libevent %s.  Available methods are:\n",
    event_get_version());
for (i=0; methods[i] != NULL; ++i) {
    
    
    printf("    %s\n", methods[i]);
}

这个函数返回 libevent 被编译以支持的方法列表 。然而 libevent 运行的时候,操作系统可能 不能支持所有方法。比如说,可能
OS X 版本中的 kqueue 的 bug 太多,无法使用。

接口二

const char *
event_base_get_method(const struct event_base *base);
enum event_method_feature 
event_base_get_features(const struct event_base *base);

event_base_get_method()返回 event_base 正在使用的方法。

event_base_get_features ()返回 event_base 支持的特征的比特掩码。

demo

struct event_base *base;
enum event_method_feature f;
base = event_base_new();
if (!base) {
    
    
    puts("Couldn't get an event_base!");
} else {
    
    
    printf("Using Libevent with backend method %s.",
        event_base_get_method(base));
    f = event_base_get_features(base);
    if ((f & EV_FEATURE_ET))
        printf("  Edge-triggered events are supported.");
    if ((f & EV_FEATURE_O1))
        printf("  O(1) event notification is supported.");
    if ((f & EV_FEATURE_FDS))
        printf("  All FD types are supported.");
    puts("");
}

使用完event_base后,要释放。

void event_base_free(struct event_base *base);

注意:这个函数不会释放当前与 event_base 关联的任何事件,或者关闭他们的套接字 ,或 者释放任何指针。

事件优先级

libevent支持为事件设置多个优先级。然而, event_base默认只支持单个优先级。可以调用 event_base_priority_init()设置 event_base 的优先级数目。

int event_base_priority_init(struct event_base *base, int n_priorities);

功时这个函数返回 0,失败时返回 -1。base 是要修改的 event_base,n_priorities 是要支持的优先级数目,这个数目至少是 1 。每个新的事件可用的优先级将从 0 (最高) 到 n_priorities-1(最低)。 必须在任何事件激活之前调用这个函数,最好在创建 event_base 后立刻调用。

event_base和fork

通常情况下,当我们调用了fork或者其他系统调用启动了新进程后,希望在新进程中继续使用event_base,就要重新初始化。

int event_reinit(struct event_base *base);

demo

struct event_base *base = event_base_new();
/* ... add some events to the event_base ... */
if (fork()) {
    
    
    /* In parent */
    continue_running_parent(base); /*...*/
} else {
    
    
    /* In child */
    event_reinit(base);
    continue_running_child(base); /*...*/
}

事件循环

当event_base已经注册了某些事件,就要通过循环不断等待事件的发生,有事件发生时,通知事件的发生。

#define EVLOOP_ONCE             0x01
#define EVLOOP_NONBLOCK         0x02
#define EVLOOP_NO_EXIT_ON_EMPTY 0x04
int event_base_loop(struct event_base *base, int flags);

在 flags 参数中设置一个或者多个标志就可以改变event_base_loop()的行为。
如果设置了 EVLOOP_ONCE ,循环将等待某些事件成为激活的 ,执行激活的事件直到没有更多的事件可以执行,然会返回。
如果设置了EVLOOP_NONBLOCK,循环不会等待事件被触发: 循环将仅仅检测是否有事件已经就绪,可以立即触发,如果有,则执行事件的回调。

完成工作后,如果正常退出, event_base_loop()返回0;如果因为后端中的某些未处理 错误而退出,则返回 -1。

event_loop算法概要

while (any events are registered with the loop,
        or EVLOOP_NO_EXIT_ON_EMPTY was set) {
    
    
    if (EVLOOP_NONBLOCK was set, or any events are already active)
        If any registered events have triggered, mark them active.
    else
        Wait until at least one event has triggered, and mark it active.
    for (p = 0; p < n_priorities; ++p) {
    
    
       if (any event with priority of p is active) {
    
    
          Run all active events with priority of p.
          break; /* Do not run any events of a less important priority */
       }
    }
    if (EVLOOP_ONCE was set or EVLOOP_NONBLOCK was set)
       break;
}

为了方便起见,也可以调用无标志的loop。event_base_dispatch ()将一直运行,直到没有已经注册的事件了,或者调用 了 event_base_loopbreak()或者 event_base_loopexit()为止。

int event_base_dispatch(struct event_base *base);

停止循环

int event_base_loopexit(struct event_base *base,
                        const struct timeval *tv);
int event_base_loopbreak(struct event_base *base);

**event_base_loopexit()**让 event_base 在给定时间之后停止循环。如果 tv 参数为 NULL, event_base 会立即停止循环,没有延时。

如果 event_base 当前正在执行任何激活事件的回调,则回调会继续运行,直到运行完所有激活事件的回调之才退出。

**event_base_loopbreak ()**让 event_base 立即退出循环。它与 event_base_loopexit (base,NULL)的不同在于,如果 event_base 当前正在执行激活事件的回调 ,它将在执行完当前正在处理的事件后立即退出。

注意 event_base_loopexit(base,NULL) 和 event_base_loopbreak(base)
在事件循环没有运行时的行为不同:前者安排下一次事件循环在下一轮回调完成后立即停止(就好像带 EVLOOP_ONCE
标志调用一样);后者却仅仅停止当前正在运行的循环,如果事件循环没有运行,则没有任何效果。

demo1


#include <event2/event.h>
/* Here's a callback function that calls loopbreak */
void cb(int sock, short what, void *arg)
{
    
    
    struct event_base *base = arg;
    event_base_loopbreak(base);
}
void main_loop(struct event_base *base, evutil_socket_t watchdog_fd)
{
    
    
    struct event *watchdog_event;
    /* Construct a new event to trigger whenever there are any bytes to
       read from a watchdog socket.  When that happens, we'll call the
       cb function, which will make the loop exit immediately without
       running any other active events at all.
     */
    watchdog_event = event_new(base, watchdog_fd, EV_READ, cb, base);
    event_add(watchdog_event, NULL);
    event_base_dispatch(base);
}

demo2:execute loop for 10 sec and break

#include <event2/event.h>
void run_base_with_ticks(struct event_base *base)
{
    
    
  struct timeval ten_sec;
  ten_sec.tv_sec = 10;
  ten_sec.tv_usec = 0;
  /* Now we run the event_base for a series of 10-second intervals, printing
     "Tick" after each.  For a much better way to implement a 10-second
     timer, see the section below about persistent timer events. */
  while (1) {
    
    
     /* This schedules an exit ten seconds from now. */
     event_base_loopexit(base, &ten_sec);
     event_base_dispatch(base);
     puts("Tick");
  }
}

有时候需要知道对 event_base_dispatch()或者 event_base_loop()的调用是正常退出 的,还是因为调用 event_base_loopexit()或者 event_base_break()而退出的。可以调 用下述函数来确定是否调用了 loopexit 或者 break函数。

int event_base_got_exit(struct event_base *base);
int event_base_got_break(struct event_base *base);

这两个函数分别会在循环是因为调用 event_base_loopexit()或者 event_base_break()而退出的时候返回 true,否则返回 false。下次启动事件循环的时候,这些值会被重设。

转存event_base的状态

为帮助调试程序(或者调试 libevent),有时候可能需要加入到 event_base 的事件及其状态 的完整列表。调用 event_base_dump_events()可以将这个列表输出到指定的文件中。

void event_base_dump_events(struct event_base *base, FILE *f);

这个列表是人可读的,未来libevent会改变它的格式

事件event

一个event_base控制着一系列的事件,这个事件的结构就是event。
libevent 的基本操作单元是事件。每个事件代表一组条件的集合,这些条件包括:

  • 文件描述符已经就绪,可以读取或者写入
  • 文件描述符变为就绪状态,可以读取或者写入(仅对于边沿触发 IO)
  • 超时事件
  • 发生某信号
  • 用户触发事件

event结构体源码如下

struct event {
    
    
	struct event_callback ev_evcallback;

	/* for managing timeouts */
	union {
    
    
		TAILQ_ENTRY(event) ev_next_with_common_timeout;
		int min_heap_idx;
	} ev_timeout_pos;
	evutil_socket_t ev_fd;

	struct event_base *ev_base;

	union {
    
    
		/* used for io events */
		struct {
    
    
			LIST_ENTRY (event) ev_io_next;
			struct timeval ev_timeout;
		} ev_io;

		/* used by signal events */
		struct {
    
    
			LIST_ENTRY (event) ev_signal_next;
			short ev_ncalls;
			/* Allows deletes in callback */
			short *ev_pncalls;
		} ev_signal;
	} ev_;

	short ev_events;
	short ev_res;		/* result passed to event callback */
	struct timeval ev_timeout;
};

可以看到它还包含了一个用于存储回调函数的结构体 event_callback。

struct event_callback {
    
    
	TAILQ_ENTRY(event_callback) evcb_active_next;
	short evcb_flags;
	ev_uint8_t evcb_pri;	/* smaller numbers are higher priority */
	ev_uint8_t evcb_closure;
	/* allows us to adopt for different types of events */
        union {
    
    
		void (*evcb_callback)(evutil_socket_t, short, void *);
		void (*evcb_selfcb)(struct event_callback *, void *);
		void (*evcb_evfinalize)(struct event *, void *);
		void (*evcb_cbfinalize)(struct event_callback *, void *);
	} evcb_cb_union;
	void *evcb_arg;
};

生成新事件

event_new():

#define EV_TIMEOUT      0x01
#define EV_READ         0x02
#define EV_WRITE        0x04
#define EV_SIGNAL       0x08
#define EV_PERSIST      0x10
#define EV_ET           0x20
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);

event_new()试图分配和构造一个用于 base 的新的事件。what 参数是上述标志的集合。

如果 fd 非负,则它是将被观察其读写事件的文件。

事件被激活时, libevent 将调用 cb 函数,

传递这些参数:文件描述符 fd,表示所有被触发事件的位字段 ,以及构造事件时的 arg 参数。

发生内部错误,或者传入无效参数时, event_new()将返回 NULL。

(1)所有新创建的事件都处于已初始化和非未决状态 ,调用 event_add()可以使其成为未决的。
(2)要释放事件,调用 event_free()。对未决或者激活状态的事件调用 event_free()是安全 的:在释放事件之前,函数将会使事件成为非激活和非未决的。

demo

#include <event2/event.h>
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();
        /* The caller has already set up fd1, fd2 somehow, and make them
           nonblocking. */
        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);
}

事件标志

  • EV_TIMEOUT
    这个标志表示某超时时间流逝后事件成为激活的。构造事件的时候,EV_TIMEOUT 标志是被忽略的:可以在添加事件的时候设置超时 ,也可以不设置。超时发生时,回调函数的 what 参数将带有这个标志。

  • EV_READ
    表示指定的文件描述符已经就绪,可以读取的时候,事件将成为激活的。

  • EV_WRITE
    表示指定的文件描述符已经就绪,可以写入的时候,事件将成为激活的。

  • EV_SIGNAL用于实现信号检测,请看下面的 “构造信号事件”节。

  • EV_PERSIST表示事件是“持久的”,请看下面的“关于事件持久性”节。

  • EV_ET
    表示如果底层的 event_base 后端支持边沿触发事件,则事件应该是边沿触发的。这个标志 影响 EV_READ 和 EV_WRITE 的语义。

事件的未决和非未决

构造事件之后,在将其添加到 event_base 之前实际上是不能对其做任何操作的。使用event_add()将事件添加到 event_base。

设置未决事件

int event_add(struct event *ev, const struct timeval *tv);
  • 在非未决的事件上调用 event_add()将使其在配置的 event_base 中成为未决的。成功时 函数返回0,失败时返回-1。

  • 如果对已经未决的事件调用 event_add(),事件将保持未决状态,并在指定的超时时间被重新调度。

设置非未决事件

int event_del(struct event *ev);

对已经初始化的事件调用 event_del()将使其成为非未决和非激活的。如果事件不是未决的或者激活的,调用将没有效果。成功时函数返回 0,失败时返回-1。

注意:如果在事件激活后,其回调被执行前删除事件,回调将不会执行。

事件优先级

我们对事件也可设置优先级,它的数值是event_base-1到0 。
多个不同优先级的事件同时成为激活的时候 ,低优先级的事件不会运行 。libevent 会执行高优先级的事件,然后重新检查各个事件。只有在没有高优先级的事件是激活的时候 ,低优先级的事件才会运行。

int event_priority_set(struct event *event, int priority);

demo

#include <event2/event.h>
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);
  /* Now base has priority 0, and priority 1 */
  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);
  /* Now, whenever the fd is ready for writing, the write callback will
     happen before the read callback.  The read callback won't happen at
     all until the write callback is no longer active. */
}

检查事件状态

有时候需要了解事件是否已经添加,检查事件代表什么。有以下的API。

int event_pending(const struct event *ev, short what, struct timeval *tv_out);
#define event_get_signal(ev) /* ... */
evutil_socket_t event_get_fd(const struct event *ev);
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);
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);
  • event_pending()函数确定给定的事件是否是未决的或者激活的。如果是,而且 what 参 数设置了 EV_READ、EV_WRITE、EV_SIGNAL 或者 EV_TIMEOUT 等标志,则函数会返回事件当前为之未决或者激活的所有标志 。如果提供了 tv_out 参数,并且 what 参数中设置了 EV_TIMEOUT 标志,而事件当前正因超时事件而未决或者激活,则 tv_out 会返回事件 的超时值。

  • event_get_fd()和 event_get_signal()返回为事件配置的文件描述符或者信号值。

  • event_get_base()返回为事件配置的 event_base。

  • event_get_events()返回事件的标志(EV_READ、EV_WRITE 等) 。

  • event_get_callback()和 event_get_callback_arg() 返回事件的回调函数及其参数指针。

  • event_get_assignment()复制所有为事件分配的字段到提供的指针中。任何为 NULL 的参数会被忽略。

#include <event2/event.h>
#include <stdio.h>
/* Change the callback and callback_arg of 'ev', which must not be
 * pending. */
int replace_callback(struct event *ev, event_callback_fn new_callback,
    void *new_callback_arg)
{
    
    
    struct event_base *base;
    evutil_socket_t fd;
    short events;
    int pending;
    pending = event_pending(ev, EV_READ|EV_WRITE|EV_SIGNAL|EV_TIMEOUT,
                            NULL);
    if (pending) {
    
    
        /* We want to catch this here so that we do not re-assign a
         * pending event.  That would be very very bad. */
        fprintf(stderr,
                "Error! replace_callback called on a pending event!\n");
        return -1;
    }
    event_get_assignment(ev, &base, &fd, &events,
                         NULL /* ignore old callback */ ,
                         NULL /* ignore old callback argument */);
    event_assign(ev, base, fd, events, new_callback, new_callback_arg);
    return 0;
}

一次触发事件

如果不需要多次添加一个事件,或者要在添加后立即删除事件,而事件又不需要是持久的 , 则可以使用 event_base_once()。

int event_base_once(struct event_base *, evutil_socket_t, short,
  void (*)(evutil_socket_t, short, void *), void *, const struct timeval *);

不能删除或者手动激活使用 event_base_once ()插入的事件:如果希望能够取消事件, 应该使用 event_new()或者 event_assign()。

手动激活事件

极少数情况下,需要在事件的条件没有触发的时候让事件成为激活的。

void event_active(struct event *ev, int what, short ncalls);

这个函数让事件 ev 带有标志 what(EV_READ、EV_WRITE 和 EV_TIMEOUT 的组合)成 为激活的。事件不需要已经处于未决状态,激活事件也不会让它成为未决的。

事件状态间转换

在这里插入图片描述
主要有:

  • event_new 创建事件,此时事件是非未决。
  • event_add使非未决事件变成未决事件,event_del使未决事件变成非未决事件
  • 事件被触发或者使用event_active使未决事件活跃,回调完成并且persist等待事件从活跃变回未决,回调完成并且不persist等待事件从活跃变回非未决

今天主要就是看了一些API以及下载了源码看了下。大概了解了如何使用libevent。

猜你喜欢

转载自blog.csdn.net/weixin_45146520/article/details/112212201