libevent初始化
event_init
函数原型
struct event_base *event_init(void);
最后需要通过event_base_free释放event_base:
event_base_free
函数原型
void event_base_free(struct event_base *);
libevent的一个最简单的例子
#include <stdio.h> #include <event.h> int main() { struct event_base *event_base; event_base = event_init(); printf("libevent: version=%s\n", event_get_version()); printf("libevent: method=%s\n", event_get_method()); event_base_free(event_base); return 0; }
初始化代码
struct event_base * event_init(void) { struct event_base *base = event_base_new(); if (base != NULL) current_base = base; return (base); }在使用libevent之前,需要对libevent进行初始化。libevent通过event_init对libevent进行初始化。它直接调用event_base_new进行初始化,所以也可以通过event_base_new对libevent进行初始化。
不过这两种初始化方式在后面的使用上有些不同。libevent的初始化主要在event_base_new,它负责创建并初始化一个struct event_base实例。libevent的设计在初始化时还维护了一个内部全局的struct event_base实例,通过event_base_new的方式不会为这个内部全局实例赋值初始化,event_init会对这个内部全局实例赋值初始化。
下面将讲event_base_new是怎么初始化的
event_base_new初始化
struct event_base * event_base_new(void) { int i; struct event_base *base; if ((base = calloc(1, sizeof(struct event_base))) == NULL) event_err(1, "%s: calloc", __func__); event_sigcb = NULL; event_gotsig = 0; detect_monotonic(); gettime(base, &base->event_tv); min_heap_ctor(&base->timeheap); TAILQ_INIT(&base->eventqueue); base->sig.ev_signal_pair[0] = -1; base->sig.ev_signal_pair[1] = -1; base->evbase = NULL; for (i = 0; eventops[i] && !base->evbase; i++) { base->evsel = eventops[i]; base->evbase = base->evsel->init(base); } if (base->evbase == NULL) event_errx(1, "%s: no event mechanism available", __func__); if (evutil_getenv("EVENT_SHOW_METHOD")) event_msgx("libevent using: %s\n", base->evsel->name); /* allocate a single active event queue */ event_base_priority_init(base, 1); return (base); }这里首先为struct event_base实例分配相应的内存,初始化内部全局变量event_sigcb,event_gotsig,检查是否支持MONOTONIC时间,如果支持,内部全局变量use_monotonic设置为1。
遍历eventops,选择一个事件机制。初始化evsel,将第一个遍历到的struct eventop结构体类型的事件机制的操作对它进行初始化。同时调用evsel的init成员函数初始化evbase。这是一个void *类型。如果没有找到,返回错误。
对于epoll, init成员函数应该是epoll_init,初始化的时候返回一个struct epollop结构体类型的指针给evbase。
对于poll, init成员函数应该是poll_init,初始化的时候返回一个struct pollop结构体类型的指针给evbase。
对于select, init成员函数应该是select_init,初始化的时候返回一个struct selectop结构体类型的指针给evbase。
对于/dev/poll, init成员函数应该是devpoll_init,初始化的时候返回一个struct devpollop结构体类型的指针给evbase。
对于kqueue, init成员函数应该是kq_init,初始化的时候返回一个struct kqop结构体类型的指针给evbase。
对于端口, init成员函数应该是evport_init,初始化的时候返回一个struct evport_data结构体类型的指针给evbase。
初始化一组尾队列activequeues,nactivequeues表示队列个数,默认为1个。通过TAILQ_INIT对每个尾队列进行初始化,队头元素指针tqh_first指向NULL,队尾元素指针tqh_last指向队头元素指针tqh_first。
这组尾队列activequeues实际代表了不同优先级的事件队列。
初始化事件结构代码
event_set
函数原型
void event_set(struct event *, int, short, void (*)(int, short, void *), void *);
void event_set(struct event *ev, int fd, short events, void (*callback)(int, short, void *), void *arg) { /* Take the current base - caller needs to set the real base later */ ev->ev_base = current_base; ev->ev_callback = callback; ev->ev_arg = arg; ev->ev_fd = fd; ev->ev_events = events; ev->ev_res = 0; ev->ev_flags = EVLIST_INIT; ev->ev_ncalls = 0; ev->ev_pncalls = NULL; min_heap_elem_init(ev); /* by default, we put new events into the middle priority */ if(current_base) ev->ev_pri = current_base->nactivequeues/2; }这里主要初始化事件,以及触发事件的fd, 触发事件后的回调函数,以及传入的参数。另外就是,初始化事件的优先级,对应优先级的事件放在libevent初始化时的那组优先级队列(尾队列)对应的那个队列。默认情况下,优先级初始化为中间优先级。
int event_base_set(struct event_base *base, struct event *ev) { /* Only innocent events may be assigned to a different base */ if (ev->ev_flags != EVLIST_INIT) return (-1); ev->ev_base = base; ev->ev_pri = base->nactivequeues/2; return (0); }
添加事件
event_add
函数原型
int event_add(struct event *ev, const struct timeval *timeout);
int event_add(struct event *ev, const struct timeval *tv) { struct event_base *base = ev->ev_base; const struct eventop *evsel = base->evsel; void *evbase = base->evbase; int res = 0; event_debug(( "event_add: event: %p, %s%s%scall %p", ev, ev->ev_events & EV_READ ? "EV_READ " : " ", ev->ev_events & EV_WRITE ? "EV_WRITE " : " ", tv ? "EV_TIMEOUT " : " ", ev->ev_callback)); assert(!(ev->ev_flags & ~EVLIST_ALL)); /* * prepare for timeout insertion further below, if we get a * failure on any step, we should not change any state. */ if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) { if (min_heap_reserve(&base->timeheap, 1 + min_heap_size(&base->timeheap)) == -1) return (-1); /* ENOMEM == errno */ } if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) && !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) { res = evsel->add(evbase, ev); if (res != -1) event_queue_insert(base, ev, EVLIST_INSERTED); } /* * we should change the timout state only if the previous event * addition succeeded. */ if (res != -1 && tv != NULL) { struct timeval now; /* * we already reserved memory above for the case where we * are not replacing an exisiting timeout. */ if (ev->ev_flags & EVLIST_TIMEOUT) event_queue_remove(base, ev, EVLIST_TIMEOUT); /* Check if it is active due to a timeout. Rescheduling * this timeout before the callback can be executed * removes it from the active list. */ if ((ev->ev_flags & EVLIST_ACTIVE) && (ev->ev_res & EV_TIMEOUT)) { /* See if we are just active executing this * event in a loop */ if (ev->ev_ncalls && ev->ev_pncalls) { /* Abort loop */ *ev->ev_pncalls = 0; } event_queue_remove(base, ev, EVLIST_ACTIVE); } gettime(base, &now); evutil_timeradd(&now, tv, &ev->ev_timeout); event_debug(( "event_add: timeout in %ld seconds, call %p", tv->tv_sec, ev->ev_callback)); event_queue_insert(base, ev, EVLIST_TIMEOUT); } return (res); }如果初始化事件指定了EV_READ,EV_WRITE,EV_SIGNAL,并且事件标识不是EVLIST_INSERTED,EVLIST_ACTIVE,调用struct event_base实例的evsel成员的add函数添加事件。这个struct event_base实例是事件的ev_base成员,在初始化事件(event_set)的时候会设置,通常是内部全局的struct event_base实例,如果是通过event_base_new初始化libevent的话,则是通过调用event_base_set设置的事件的ev_base,指向初始化的struct event_base实例。
对于epoll, add成员函数应该是epoll_add。
对于poll, add成员函数应该是poll_add。
对于select, add成员函数应该是select_add。
对于/dev/poll, add成员函数应该是devpoll_add。
对于kqueue, add成员函数应该是kq_add。
对于端口, add成员函数应该是evport_add。
如果调用add函数添加事件成功,同时调用event_queue_insert函数将事件添加到队列中,也就是struct event_base实例的eventqueue成员,这是一个尾队列。
如果调用add函数添加事件成功,并且指定了超时时间tv的话:
如果事件标识EVLIST_TIMEOUT,将事件从堆timeheap上删除。
如果事件标识EVLIST_ACTIVE,表示事件被激活触发,且事件回调函数调用后返回超时EV_TIMEOUT,如果事件的ev_ncalls不为0,且ev_pncalls不为NULL,则将ev_pncalls指向的变量置为0。然后调用event_queue_remove函数从activequeues队列(尾队列)中删除事件。
设置事件的timeout。
将事件加入到堆timeheap中。
static void detect_monotonic(void) { #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) use_monotonic = 1; #endif }这里就是检查是否可以正常获取MONOTONIC时间。
HAVE_CLOCK_GETTIME
执行./autogen.sh后,会生成一个config.h.in配置文件。
cat config.h.in
/* Define to 1 if you have the `clock_gettime' function. */
#undef HAVE_CLOCK_GETTIME
再执行./configure后,会生成一个config.h头文件。
cat config.h
/* Define to 1 if you have the `clock_gettime' function. */
#define HAVE_CLOCK_GETTIME 1
执行./configure的时候,会检查clock_gettime
checking for clock_gettime... yes
CLOCK_MONOTONIC
这个在bits/time.h头文件中有定义
/* Monotonic system-wide clock. */
# define CLOCK_MONOTONIC 1
表示从某个起始点开始的单调时间
epoll
初始化
poll
初始化
select
初始化
/dev/poll
初始化
kqueue
初始化
端口
初始化
相关推荐
- **源代码阅读**:深入理解libevent的源代码,有助于提升开发水平和解决复杂问题的能力。 通过以上内容,我们可以全面了解libevent的基本原理和使用方法,对于开发高并发、高性能的网络服务来说,libevent无疑是...
注解可能解释了如何初始化和管理这个结构,以及如何调度和运行事件。`event_add()`和`event_del()`函数分别用于添加和删除事件到事件基础结构中,注解会解释它们的工作方式和参数含义。 此外,libevent支持多种事件...
这个压缩包包含两个版本的Libevent源代码,分别是libevent-1.4.9-stable.zip和libevent-1.4.12-stable.tar.gz。其中,libevent-1.4.9-stable.zip版本带有中文注释,对于中国开发者来说,理解源码会更加方便。而...
2. **下载源码**:从官方网站或者GitHub仓库获取libevent-2.1.9-beta的源代码,并解压至本地。 3. **编译openssl-1.0.2o**:libevent依赖openssl进行加密通信,因此先要编译openssl。执行configure脚本,指定目标...
1. **初始化libevent**:调用`event_init`函数初始化libevent环境。 2. **注册事件**:使用`event_new`创建事件,`event_set`设置事件类型(读、写、信号等)及回调函数。 3. **添加到事件循环**:使用`event_add`将...
然后,在代码中引入必要的头文件,例如`#include <event2/event.h>`和`#include <openssl/ssl.h>`,并调用相应的API来初始化libevent和OpenSSL,创建事件基础结构,设置事件处理回调函数。 在Win64环境下,libevent...
三、Libevent源代码解析 1. **初始化Libevent**:在服务器启动时,首先需要调用`event_base_new()`创建一个事件基础结构,然后设置事件处理回调函数。 2. **监听套接字**:通过`socket()`创建监听套接字,使用`...
信号event是libevent中处理信号事件的机制,包括初始化、设置捕捉函数、激活信号event等步骤。 15. evthread_notify_base 通知主线程的机制确保了多线程环境下的协调和同步。 16. 超时event的处理 超时event机制...
Libevent 分析涉及到对源代码的深入理解,以便揭示其工作原理、性能优化策略以及如何有效地利用它来构建应用程序。 1. **事件模型** Libevent 支持多种事件模型,包括基于 select、poll、epoll(Linux)、kqueue...
在项目中,`libevent-client`和`libevent-server`分别代表了客户端和服务器的源代码文件,包含了上述过程的实现细节。通过阅读和分析这些代码,可以更深入地理解libevent在UDP relay场景中的应用。
在代码中,你需要包含libevent的头文件并初始化libevent库。例如: ```cpp #include int main() { event_base* base = event_base_new(); if (base == NULL) { // 错误处理 } // 使用libevent进行事件处理....
在《memcached关键数据结构.txt》文件中,可能详细介绍了这些数据结构的定义、用途和实现细节,包括如何初始化slab类分配器、哈希表的构建与查找、item的创建与销毁等。 **四、分析PPT** 《memcached源代码分析1....
在主循环中,事件被分为了多个阶段,如初始化、时间事件处理、I/O事件处理、清理等,确保了事件处理的有序性。 4. **事件主循环** 事件主循环的每个阶段都有其特定的任务。在时间事件阶段,libevent会检查并触发...
压缩包中的"libevet_server_client"很可能包含了服务器和客户端的源代码文件,可能包括`.c`或`.cpp`文件,以及编译和运行所需的Makefile或其他构建脚本。这些文件可以作为学习和参考的实例,通过阅读和运行代码,...
- **初始化libevent**:调用`event_base_new()`创建一个事件基础,是libevent的第一步。 - **添加事件**:使用`event_new()`创建事件,然后通过`event_add()`将事件添加到事件基础中。 - **设置回调函数**:为...
4. **添加源代码**:将解压后的libevent源代码文件夹中的所有源文件(.c和.h文件)添加到项目中。 5. **设置预处理器定义**:在项目属性中,选择“C/C++”->“预处理器”,添加相应的预处理器定义,例如对于...
1. **初始化**:调用 `event_init` 函数初始化 libevent。 2. **创建事件**:为每个事件源(如 FD)创建一个事件结构体,设置事件类型、回调函数等。 3. **添加事件**:使用 `event_add` 或 `event_del` 将事件添加...
在这些源码中,我们可以找到如何初始化线程池,如何注册libevent事件,以及如何在事件触发时调用适当的处理函数等关键细节。 总结来说,"线程池方式的libevent-server.zip"是一个利用libevent和线程池技术构建的高...
首先,可以从主线程的初始化开始,理解事件基结构的创建和配置。然后,关注事件源的注册,尤其是事件类型的选择和回调函数的设定。接着,深入到事件循环的运行机制,理解何时及如何处理事件。最后,查看libevent如何...
这些源代码文件应该展示了如何使用`libevent`库创建基本的TCP客户端和服务器。 - 客户端程序可能包含了建立连接、发送数据和关闭连接的逻辑,而服务器程序则包含了监听连接、接收数据、处理数据和发送响应的流程。 ...