`
lobin
  • 浏览: 417719 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

libevent: 源代码分析-libevent初始化

 
阅读更多

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。
调用gettime获取当前时间,先从tv_cache缓冲中获取,不过初始化的时候tv_cache的tv_sec通常为0,不会从缓存中取,如果支持支持MONOTONIC时间,则获取的是MONOTONIC时间,否则调用evutil_gettimeofday通过gettimeofday或者_ftime获取。
调用min_heap_ctor创建一个min_heap,初始化timeheap
初始化尾队列eventqueue
初始化sig的ev_signal_pair成员

遍历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初始化时的那组优先级队列(尾队列)对应的那个队列。默认情况下,优先级初始化为中间优先级。
事件的ev_base指向内部全局的struct event_base实例。
如果是通过event_base_new初始化libevent的话,需要通过调用event_base_set将设置事件的ev_base,指向初始化的struct event_base实例。
event_base_set
函数原型
int event_base_set(struct event_base *, struct event *);
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_base_set除了设置事件的ev_base,也初始化事件的优先级,对应优先级的事件放在libevent初始化时的那组优先级队列(尾队列)对应的那个队列。默认情况下,优先级初始化为中间优先级。
 
 

添加事件

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官方文档 Ref1-9

    - **源代码阅读**:深入理解libevent的源代码,有助于提升开发水平和解决复杂问题的能力。 通过以上内容,我们可以全面了解libevent的基本原理和使用方法,对于开发高并发、高性能的网络服务来说,libevent无疑是...

    libevent源代码, 其中代码中包含注解

    注解可能解释了如何初始化和管理这个结构,以及如何调度和运行事件。`event_add()`和`event_del()`函数分别用于添加和删除事件到事件基础结构中,注解会解释它们的工作方式和参数含义。 此外,libevent支持多种事件...

    libevent源代码(包含中文注释)

    这个压缩包包含两个版本的Libevent源代码,分别是libevent-1.4.9-stable.zip和libevent-1.4.12-stable.tar.gz。其中,libevent-1.4.9-stable.zip版本带有中文注释,对于中国开发者来说,理解源码会更加方便。而...

    test_libevent-2.1.9-beta

    2. **下载源码**:从官方网站或者GitHub仓库获取libevent-2.1.9-beta的源代码,并解压至本地。 3. **编译openssl-1.0.2o**:libevent依赖openssl进行加密通信,因此先要编译openssl。执行configure脚本,指定目标...

    libevent-2.0.19-stable.tar.gz

    1. **初始化libevent**:调用`event_init`函数初始化libevent环境。 2. **注册事件**:使用`event_new`创建事件,`event_set`设置事件类型(读、写、信号等)及回调函数。 3. **添加到事件循环**:使用`event_add`将...

    libevent-withOpenssl-win64-binary

    然后,在代码中引入必要的头文件,例如`#include &lt;event2/event.h&gt;`和`#include &lt;openssl/ssl.h&gt;`,并调用相应的API来初始化libevent和OpenSSL,创建事件基础结构,设置事件处理回调函数。 在Win64环境下,libevent...

    libevent的视频服务器源代码

    三、Libevent源代码解析 1. **初始化Libevent**:在服务器启动时,首先需要调用`event_base_new()`创建一个事件基础结构,然后设置事件处理回调函数。 2. **监听套接字**:通过`socket()`创建监听套接字,使用`...

    libevent源码分析

    信号event是libevent中处理信号事件的机制,包括初始化、设置捕捉函数、激活信号event等步骤。 15. evthread_notify_base 通知主线程的机制确保了多线程环境下的协调和同步。 16. 超时event的处理 超时event机制...

    Libevent_Analysis:Libevent网络框架源代码分析

    Libevent 分析涉及到对源代码的深入理解,以便揭示其工作原理、性能优化策略以及如何有效地利用它来构建应用程序。 1. **事件模型** Libevent 支持多种事件模型,包括基于 select、poll、epoll(Linux)、kqueue...

    VS2015编译后的libevent头文件和库文件——基于libevent-2.1.10

    在代码中,你需要包含libevent的头文件并初始化libevent库。例如: ```cpp #include int main() { event_base* base = event_base_new(); if (base == NULL) { // 错误处理 } // 使用libevent进行事件处理....

    libevent实现UDP relay服务器与客户端

    在项目中,`libevent-client`和`libevent-server`分别代表了客户端和服务器的源代码文件,包含了上述过程的实现细节。通过阅读和分析这些代码,可以更深入地理解libevent在UDP relay场景中的应用。

    memcached源代码分析

    在《memcached关键数据结构.txt》文件中,可能详细介绍了这些数据结构的定义、用途和实现细节,包括如何初始化slab类分配器、哈希表的构建与查找、item的创建与销毁等。 **四、分析PPT** 《memcached源代码分析1....

    libevent源码分析1

    在主循环中,事件被分为了多个阶段,如初始化、时间事件处理、I/O事件处理、清理等,确保了事件处理的有序性。 4. **事件主循环** 事件主循环的每个阶段都有其特定的任务。在时间事件阶段,libevent会检查并触发...

    libevent服务器和客户端

    压缩包中的"libevet_server_client"很可能包含了服务器和客户端的源代码文件,可能包括`.c`或`.cpp`文件,以及编译和运行所需的Makefile或其他构建脚本。这些文件可以作为学习和参考的实例,通过阅读和运行代码,...

    libevent-中文参考手册.7z

    - **初始化libevent**:调用`event_base_new()`创建一个事件基础,是libevent的第一步。 - **添加事件**:使用`event_new()`创建事件,然后通过`event_add()`将事件添加到事件基础中。 - **设置回调函数**:为...

    VS2013使用libevent2.1.8编写简单服务器代码

    4. **添加源代码**:将解压后的libevent源代码文件夹中的所有源文件(.c和.h文件)添加到项目中。 5. **设置预处理器定义**:在项目属性中,选择“C/C++”-&gt;“预处理器”,添加相应的预处理器定义,例如对于...

    libevent 注释

    1. **初始化**:调用 `event_init` 函数初始化 libevent。 2. **创建事件**:为每个事件源(如 FD)创建一个事件结构体,设置事件类型、回调函数等。 3. **添加事件**:使用 `event_add` 或 `event_del` 将事件添加...

    线程池方式的libevent-server.zip

    在这些源码中,我们可以找到如何初始化线程池,如何注册libevent事件,以及如何在事件触发时调用适当的处理函数等关键细节。 总结来说,"线程池方式的libevent-server.zip"是一个利用libevent和线程池技术构建的高...

    libevent:配合网上教程阅读libevent原始档案,取代原始档案进行注释

    首先,可以从主线程的初始化开始,理解事件基结构的创建和配置。然后,关注事件源的注册,尤其是事件类型的选择和回调函数的设定。接着,深入到事件循环的运行机制,理解何时及如何处理事件。最后,查看libevent如何...

    运用libevent库实现客户服务器模式

    这些源代码文件应该展示了如何使用`libevent`库创建基本的TCP客户端和服务器。 - 客户端程序可能包含了建立连接、发送数据和关闭连接的逻辑,而服务器程序则包含了监听连接、接收数据、处理数据和发送响应的流程。 ...

Global site tag (gtag.js) - Google Analytics