libevent (hello-world代码源码)----event_assign

evconnlistener_new_bind中还差一个event_assign函数没看,现在来看一下:

先回一下函数定义:

/**
准备要添加的新的、已分配的事件结构。
函数event_assign()准备事件结构ev,以便在将来调用event_ add()和eventU del()时使用。
与event_new()不同,它本身不分配内存:它要求您已经分配了一个struct事件,可能是在堆上。
这样做通常会使代码取决于事件结构的大小,从而导致与Libevent的未来版本不兼容。
避免此问题的最简单方法就是使用event_new()和event_ free()。
要证明代码的未来性,一种稍微困难的方法是使用event_get_struct_event_ size()来确定运行时所需的事件大小。
请注意,在活动或挂起的事件上调用此函数是不安全的。
这样做会破坏Libevent中的内部数据结构,并导致奇怪的、难以诊断的错误!
此函数的参数及其生成的事件行为与event_new()相同。
@param ev:要修改的事件结构
@param base ev应连接到的事件库。
@param fd:要监视的文件描述符
@参数事件:需要监视的事件;可以是EV_ READ和/或EV_
@事件发生时要调用的param回调函数
@param callback_arg:要传递给回调函数的参数
@如果成功,则返回0;如果参数无效,则返回-1。
@请参见event_new()、event_ add()、event_del()、event_base_once()、evelt_get_struct_
*/
EVENT2_EXPORT_SYMBOL

int event_assign(struct event *, struct event_base *, evutil_socket_t, short, event_callback_fn, void *);

 该函数主要是给event结构体初始化赋值,绑定event_base、回调函数和回调函数参数、文件描述符以及标志位,中间判断持续化事件。(ps:如果这个event是用来监听一个信号的,那么就不能让这个event监听读或者写事件。原因是其与信号event的实现方法相抵触。)

然后看下怎么实现的

int
event_assign(struct event *ev, struct event_base *base, evutil_socket_t fd, short events, void (*callback)(evutil_socket_t, short, void *), void *arg)
{
	if (!base)
		base = current_base;//NULL
	if (arg == &event_self_cbarg_ptr_)//event_self_cbarg_ptr_ NULL
		arg = ev;

	if (!(events & EV_SIGNAL))
		event_debug_assert_socket_nonblocking_(fd);//判断fd是否合法
	event_debug_assert_not_added_(ev);//判断ev是否合法,没有add(ps:请注意,在活动或挂起的事件上调用event_assign是不安全的)
	//初始化参数 绑定base,回调函数,回调函数参数,文件描述符以及标志位,中间判断持续化事件

	ev->ev_base = 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;
	//p判断监听事件、不允许监听信号后还监听读写
	if (events & EV_SIGNAL) {
		if ((events & (EV_READ|EV_WRITE|EV_CLOSED)) != 0) {
			event_warnx("%s: EV_SIGNAL is not compatible with "
			    "EV_READ, EV_WRITE or EV_CLOSED", __func__);
			return -1;
		}
		ev->ev_closure = EV_CLOSURE_EVENT_SIGNAL;
	} else {
		if (events & EV_PERSIST) {
			evutil_timerclear(&ev->ev_io_timeout);
			ev->ev_closure = EV_CLOSURE_EVENT_PERSIST;
		} else {
			ev->ev_closure = EV_CLOSURE_EVENT;
		}
	}

	min_heap_elem_init_(ev);//初始化最小堆(置-1)

	if (base != NULL) {
		/* by default, we put new events into the middle priority */
		//默认情况下,我们将新事件置于中间优先级
		ev->ev_pri = base->nactivequeues / 2;
	}
	//记录ev现在已设置(即准备添加)
	event_debug_note_setup_(ev);

	return 0;
}

然后就是add一下:

/**
   Re-enable an evconnlistener that has been disabled.
//重新启用已禁用的evconnlistener。
 */
EVENT2_EXPORT_SYMBOL
int evconnlistener_enable(struct evconnlistener *lev);

看一下是怎么做的:

int
evconnlistener_enable(struct evconnlistener *lev)
{
	int r;
	LOCK(lev);
	lev->enabled = 1;
	if (lev->cb)
		r = lev->ops->enable(lev);
	else
		r = 0;
	UNLOCK(lev);
	return r;
}

合理,改变状态值,调用enable函数。那让我们看一下enable函数:

static int
event_listener_enable(struct evconnlistener *lev)
{	
	//辅助宏。如果我们知道给定指针指向结构中的字段,则返回指向结构本身的指针。
	//根据对外提供的evconnlistener获取evconnlistener_event的地址,本来强制转换即可,但是为了可移植性
	struct evconnlistener_event *lev_e =
	    EVUTIL_UPCAST(lev, struct evconnlistener_event, base);
	return event_add(&lev_e->listener, NULL);
}

继续,里面调用了event_add函数,老规矩,先看函数定义:

/**
  Add an event to the set of pending events.

  The function event_add() schedules the execution of the event 'ev' when the
  condition specified by event_assign() or event_new() occurs, or when the time
  specified in timeout has elapsed.  If a timeout is NULL, no timeout
  occurs and the function will only be
  called if a matching event occurs.  The event in the
  ev argument must be already initialized by event_assign() or event_new()
  and may not be used
  in calls to event_assign() until it is no longer pending.

  If the event in the ev argument already has a scheduled timeout, calling
  event_add() replaces the old timeout with the new one if tv is non-NULL.

  @param ev an event struct initialized via event_assign() or event_new()
  @param timeout the maximum amount of time to wait for the event, or NULL
         to wait forever
  @return 0 if successful, or -1 if an error occurred
  @see event_del(), event_assign(), event_new()
  */
EVENT2_EXPORT_SYMBOL
int event_add(struct event *ev, const struct timeval *timeout);

百度翻译一下:

/**
将事件添加到挂起事件集。
函数event_add()在event_assign()或event_ new()指定的条件发生时,
或在超时中指定的时间已过时,调度事件“ev”的执行。
如果超时为NULL,则不会发生超时,并且仅当发生匹配事件时才会调用该函数。
ev参数中的事件必须已经由event_assign()或event_ new()初始化,
并且在不再挂起之前,不得用于调用event_assign()。
如果ev参数中的事件已经有一个计划的超时,如果tv为非空,
则调用event_add()将旧的超时替换为新的超时。
@param ev:通过event_assign()或event_ new()初始化的事件结构
@param timeout等待事件的最长时间,或NULL永远等待
@如果成功,则返回0;如果发生错误,则返回-1
@请参见event_del()、event_ assign()、event_new()
*/

然后看函数实现:

int
event_add(struct event *ev, const struct timeval *tv)
{
	int res;
	//检查是否已经绑定了ev_base,(event_assign中赋值)
	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);
	//用于添加事件的实现函数。其工作原理与event_ add类似,
	//除了:1)它要求我们有锁。2) 如果设置了tv_ is_,
	//我们将tv视为绝对时间,而不是添加到当前时间的间隔
	res = event_add_nolock_(ev, tv, 0);
	//解锁
	EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock);

	return (res);
}

检查、加锁,然后功能由event_add_nolock_函数完成,该函数实现如下:

/* Implementation function to add an event.  Works just like event_add,
 * except: 1) it requires that we have the lock.  2) if tv_is_absolute is set,
 * we treat tv as an absolute time, not as an interval to add to the current
 * time */
/*
用于添加事件的实现函数。它的工作原理与event_add类似,除了:
1) 它要求我们有锁。
2) 如果设置了tv_is_absolute, 我们将tv视为绝对时间,而不是添加到当前时间的间隔
*/

int
event_add_nolock_(struct event *ev, const struct timeval *tv,
    int tv_is_absolute)
{
	struct event_base *base = ev->ev_base;
	int res = 0;
	int notify = 0;

	EVENT_BASE_ASSERT_LOCKED(base);//检查加锁,如果启用了锁调试,并且锁不为空,则断言“锁”已锁定并由我们持有
	event_debug_assert_is_setup_(ev);//检查
	//日志
	event_debug((
		 "event_add: event: %p (fd "EV_SOCK_FMT"), %s%s%s%scall %p",
		 ev,
		 EV_SOCK_ARG(ev->ev_fd),
		 ev->ev_events & EV_READ ? "EV_READ " : " ",
		 ev->ev_events & EV_WRITE ? "EV_WRITE " : " ",
		 ev->ev_events & EV_CLOSED ? "EV_CLOSED " : " ",
		 tv ? "EV_TIMEOUT " : " ",
		 ev->ev_callback));

	EVUTIL_ASSERT(!(ev->ev_flags & ~EVLIST_ALL));//判断标签合不合法,确保event的flags是给定的几种的合法组合
	//已经处于结束状态的事件再次添加会报错
	if (ev->ev_flags & EVLIST_FINALIZING) {
		/* XXXX debug */
		return (-1);
	}

	/*
	 * 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 the main thread is currently executing a signal event's
	 * callback, and we are not the main thread, then we want to wait
	 * until the callback is done before we mess with the event, or else
	 * we can race on ev_ncalls and ev_pncalls below. */
/*
如果主线程当前正在执行信号事件的回调,而我们不是主线程,那么我们希望等到回调完成后再处理事件。
*/
#ifndef EVENT__DISABLE_THREAD_SUPPORT
	if (base->current_event == event_to_event_callback(ev) &&
	    (ev->ev_events & EV_SIGNAL)
	    && !EVBASE_IN_THREAD(base)) {
		++base->current_event_waiters;
		EVTHREAD_COND_WAIT(base->current_event_cond, base->th_base_lock);
	}
#endif
	//按照ev_flags,添加io或信号触发
	if ((ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED|EV_SIGNAL)) &&
	    !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE|EVLIST_ACTIVE_LATER))) {
		if (ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED))
			res = evmap_io_add_(base, ev->ev_fd, ev);//将IO事件(读、写)添加到给定文件描述符上的event_ base事件列表中,如果fd的状态已更改,则告知底层eventops
		else if (ev->ev_events & EV_SIGNAL)
			res = evmap_signal_add_(base, (int)ev->ev_fd, ev);//这些函数的行为方式与IO相同,只是它们处理的是信号而不是FD。信号在任何地方都使用线性映射;FD使用线性映射或哈希表
		if (res != -1)
			event_queue_insert_inserted(base, ev);//将事件处理器插入到注册事件队列
		if (res == 1) {
			/* evmap says we need to notify the main thread. */
			//evmap说我们需要通知主线程。
			notify = 1;
			res = 0;
		}
	}

	/*
	 * we should change the timeout state only if the previous event
	 * addition succeeded.
	 */
	//只有当前一个事件添加成功时,我们才应该更改超时状态。
	//在前一个事件添加成功,且该事件是一个定时器的话,将其添加至时间堆或通用定时器队列当中
	if (res != -1 && tv != NULL) {
		struct timeval now;
		int common_timeout;
#ifdef USE_REINSERT_TIMEOUT
		int was_common;
		int old_timeout_idx;
#endif

		/*
		 * for persistent timeout events, we remember the
		 * timeout value and re-add the event.
		 *
		 * If tv_is_absolute, this was already set.
		 */
		//对于持久超时事件,我们记住超时值并重新添加事件
		//If tv_is_absolute 这已经设置好了。
		if (ev->ev_closure == EV_CLOSURE_EVENT_PERSIST && !tv_is_absolute)
			ev->ev_io_timeout = *tv;

#ifndef USE_REINSERT_TIMEOUT
		//若该事件处理器已经被插入到通用定时器队列或时间堆当中,则先对其进行删除
		if (ev->ev_flags & EVLIST_TIMEOUT) {
			event_queue_remove_timeout(base, ev);
		}
#endif

		/* 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)) {
			if (ev->ev_events & EV_SIGNAL) {
				/* See if we are just active executing
				 * this event in a loop
				 */
				//看看我们是否只是在循环中执行此事件
				//若事件处理器的回调函数正在执行当中,则设置 ev_ncalls 的值为 0,就可以退出回调函数的执行循环
				if (ev->ev_ncalls && ev->ev_pncalls) {
					/* Abort loop */
					*ev->ev_pncalls = 0;
				}
			}
			//删除
			event_queue_remove_active(base, event_to_event_callback(ev));
		}

		gettime(base, &now);

		common_timeout = is_common_timeout(tv, base);用于判断应该将定时器插入到通用定时器队列还是时间堆当中
#ifdef USE_REINSERT_TIMEOUT
		was_common = is_common_timeout(&ev->ev_timeout, base);
		old_timeout_idx = COMMON_TIMEOUT_IDX(&ev->ev_timeout);
#endif

		if (tv_is_absolute) {
			ev->ev_timeout = *tv;
		} else if (common_timeout) {
			struct timeval tmp = *tv;
			tmp.tv_usec &= MICROSECONDS_MASK;
			evutil_timeradd(&now, &tmp, &ev->ev_timeout);
			ev->ev_timeout.tv_usec |=
			    (tv->tv_usec & ~MICROSECONDS_MASK);
		} else {
			//加上当前的系统时间以获得定时器超时的绝对时间
			evutil_timeradd(&now, tv, &ev->ev_timeout);
		}

		event_debug((
			 "event_add: event %p, timeout in %d seconds %d useconds, call %p",
			 ev, (int)tv->tv_sec, (int)tv->tv_usec, ev->ev_callback));
//插入定时器
#ifdef USE_REINSERT_TIMEOUT
		//根据 common_timeout 决定插入的位置是通用定时器队列还是时间堆
		event_queue_reinsert_timeout(base, ev, was_common, common_timeout, old_timeout_idx);
#else
		event_queue_insert_timeout(base, ev);
#endif

		if (common_timeout) {
			struct common_timeout_list *ctl =
			    get_common_timeout_list(base, &ev->ev_timeout);
			//若被插入的定时器是通用定时器队列的首元素,则调用 common_timeout_schedule 
			 //将其转移到时间堆单中,这样可以使得时间堆和定时器链表的定时器得到相同的处理。
			if (ev == TAILQ_FIRST(&ctl->events)) {
				common_timeout_schedule(ctl, &now, ev);
			}
		} else {
			struct event* top = NULL;
			/* See if the earliest timeout is now earlier than it
			 * was before: if so, we will need to tell the main
			 * thread to wake up earlier than it would otherwise.
			 * We double check the timeout of the top element to
			 * handle time distortions due to system suspension.
			 */
			//看看最早的超时现在是否比以前早:如果是这样,我们需要告诉主线程比以前更早唤醒。
			//我们仔细检查顶部元素的超时,以处理由于系统暂停而导致的时间扭曲。
			if (min_heap_elt_is_top_(ev))
				notify = 1;
			//若定时器不位于堆顶,则取出堆顶的定时器,判断是否小于当前时间,如果是,则通知主线程
			else if ((top = min_heap_top_(&base->timeheap)) != NULL &&
					 evutil_timercmp(&top->ev_timeout, &now, <))
				notify = 1;
		}
	}

	/* if we are not in the right thread, we need to wake up the loop */
	//如果我们没有在正确的线程中,我们需要唤醒循环,唤醒主进程
	if (res != -1 && notify && EVBASE_NEED_NOTIFY(base))
		//告诉当前运行event_loop for base(如果有)的线程,它需要停止在其调度函数中等待(如果有),并处理所有活动回调。
		evthread_notify_base(base);

	event_debug_note_add_(ev);

	return (res);
}

添加了信号、io的映射,并初始化了定时器,将其添加至时间堆或通用定时器队列。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 《libevent参考手册(中文版)_libevent-2.1.5.pdf》是libevent事件驱动库的参考手册的中文翻译版本,主要是为中国的开发者提供方便和参考。libevent是一个开的高性能网络库,广泛应用于服务器程序的开发中。 该手册详细介绍了libevent库的使用方法和相关的API函数,帮助开发者们更好地理解和使用这个库。手册的内容包括库的基本概述、事件循环、定时器、信号处理、缓冲区管理等各个方面。通过阅读手册,开发者可以学习如何使用libevent库来构建高效可靠的网络应用程序。 手册中的每个功能点都有详细的说明和示例代码,让开发者可以更快地上手使用libevent库。无论是初学者还是有一定经验的开发者,都可以通过这份参考手册快速入门和掌握libevent库的使用。 此外,手册还包含了常见问题的解答与技巧,帮助开发者在使用libevent时遇到问题时能够解决,提高开发效率。 总之,《libevent参考手册(中文版)_libevent-2.1.5.pdf》为开发者们提供了掌握libevent库的必备工具和参考资料。通过学习手册中的内容,开发者们可以更好地利用libevent库来实现高性能、高可靠性的网络应用程序。 ### 回答2: libevent是一个事件驱动的编程库,用于开发高效的网络通信应用程序。它提供了一个简单而灵活的接口,能够实现异步的、非阻塞的事件处理机制。 libevent参考手册中文版_libevent-2.1.5.pdf是libevent官方提供的参考手册的中文翻译版本。这个手册详细介绍了libevent库的各个方面,包括核心概念、函数接口、使用方法等。通过阅读这个手册,用户可以了解到如何使用libevent来开发高效的网络应用程序。 这个手册首先介绍了libevent的设计理念和特点,包括事件驱动的编程模型、事件循环机制等。然后详细介绍了libevent库中的各个功能模块,包括事件基、事件、缓冲区、定时器等。对于每个功能模块,手册提供了对应的API接口说明和使用示例。 除了功能模块的介绍外,手册还包括了一些高级话题的讨论,例如多线程编程、信号处理、SSL支持等。这些内容对于开发复杂的网络应用程序非常有用。 总的来说,libevent参考手册中文版_libevent-2.1.5.pdf是libevent库的官方文档的中文翻译版本。通过阅读这个手册,用户可以全面了解libevent库的使用方法和原理,从而能够更好地利用libevent开发高效的网络通信应用程序。 ### 回答3: libevent是一个事件驱动的网络编程库,它简化了网络应用程序的开发过程。libevent支持多种操作系统平台,包括Windows、Linux和Unix,并提供了跨平台的API接口。 libevent参考手册中文版_libevent-2.1.5.pdf是libevent库的中文参考手册,它详细介绍了libevent库的各个功能和使用方法,对于开发人员来说是非常有用的工具。 这个参考手册主要包含以下几个部分: 1. 简介:介绍了libevent库的概要和功能特点,以及它在网络编程中的作用。 2. 安装和配置:详细说明了如何安装和配置libevent库,包括下载代码、编译安装和设置环境变量等步骤。 3. API接口:列举了libevent库提供的各种API接口,并对每个接口进行了详细的说明。开发人员可以根据自己的需求选择合适的接口来实现网络应用程序。 4. 示例代码:提供了一些实用的示例代码,演示了如何使用libevent库来开发各种类型的网络应用程序,如服务器、客户端和代理等。 5. 常见问题解答:收集了一些常见的问题和解答,帮助开发人员在使用libevent库过程中遇到问题时能够及时获取帮助。 通过学习和参考libevent参考手册,开发人员可以更好地理解和掌握libevent库的使用方法,提高网络应用程序的开发效率。同时,中文版的参考手册方便了不擅长英文阅读的开发人员使用libevent库进行开发工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值