libevent源码学习研究(libevent-0.1)

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">想学习研究libevent怎么设计的,学习它的思想,学习它的设计,奈何自己实力不够啊,于是另辟奇径,从最早的版本开始,一个版本一个版本的学习,不信吃不透它。</span>

struct event {
	TAILQ_ENTRY (event) ev_read_next;
	TAILQ_ENTRY (event) ev_write_next;
	TAILQ_ENTRY (event) ev_timeout_next;
	TAILQ_ENTRY (event) ev_add_next;

	int ev_fd;
	short ev_events;

	struct timeval ev_timeout;

	void (*ev_callback)(int, short, void *arg);
	void *ev_arg;

	int ev_flags;
};

以上为事件的结构体,libevent通过这个结构体管理事件

void event_init(void);
int event_dispatch(void);

int timeout_next(struct timeval *);
void timeout_process(void);
void event_set(struct event *, int, short, void (*)(int, short, void *), void *);
void event_add(struct event *, struct timeval *);
void event_del(struct event *);

int event_pending(struct event *, short, struct timeval *);

以上为libevent提供的接口,下面我们一个一个的详细分析他们

首先,事件初始化,初始化libevent

/*四个队列的头指针*/
TAILQ_HEAD (timeout_list, event) timequeue;
TAILQ_HEAD (event_wlist, event) writequeue;
TAILQ_HEAD (event_rlist, event) readqueue;
TAILQ_HEAD (event_ilist, event) addqueue;

接着是libevent的核心循环函数

/*
	事件处理的主循环:int event_dispatch(void)
	功能描述:事件处理的主循环,循环监听,调用事件的回调函数处理
	函数实现:1.开始循环之前,先调用函数event_recalc()分配合适的fd_set
			  2.进入处理循环
				(1).遍历读事件队列和写事件队列,将描述符添加到fd_set中
				(2).调用timeout_next(),得到阻塞等待时间
				(3).调用select监听
				(4).遍历读写队列,看看有那个事件被触发,调用对应的回调函数,并将其从队列中移出
					如果没有被触发,它们将仍被保留在队列中,这个时候我们从它们中再次找到最大文件描述符
					并且,在处理期间,要将inloop标记置位,防止在读写队列期间,再将新的事件移入队列
				(5).处理完之后,遍历add队列,将add队列中的事件,对应投入读或者写队列中
				(6).根据新的最大描述符,调用event_recalc()重新分配fd_set空间
				(7).调用timeout_process()处理

*/
int
event_dispatch(void)
{
	struct timeval tv;
	struct event *ev, *old;
	int res, maxfd;

	/* Calculate the initial events that we are waiting for */
	//1.开始循环之前,重新计算分配描述符集合大小
	if (events_recalc(0) == -1)
		return (-1);

	while (1) {
		//将读写事件集全部清0,类似于FD_ZERO
		memset(event_readset, 0, event_fdsz);
		memset(event_writeset, 0, event_fdsz);

		//遍历写事件集合队列
		TAILQ_FOREACH(ev, &writequeue, ev_write_next)
				//将写事件集合中的描述符,添加道select监听的写数据集
				FD_SET(ev->ev_fd, event_writeset);
		//遍历读事件集合队列
		TAILQ_FOREACH(ev, &readqueue, ev_read_next)
				//遍历读事件集合队列,将事件描述符添加到select监听
				FD_SET(ev->ev_fd, event_readset);

		//时间设置
		timeout_next(&tv);

		if ((res = select(event_fds + 1, event_readset,
				  event_writeset, NULL, &tv)) == -1) {
			if (errno != EINTR) {
				log_error("select");
				return (-1);
			}
			continue;
		}

		LOG_DBG((LOG_MISC, 80, __FUNCTION__": select reports %d",
			 res));

		maxfd = 0;
		event_inloop = 1;
		for (ev = TAILQ_FIRST(&readqueue); ev;) {
			//从读事件队头拿出读事件
			old = TAILQ_NEXT(ev, ev_read_next);
			if (FD_ISSET(ev->ev_fd, event_readset)) {
				//看看这个事件是否就绪
				event_del(ev);
				//从事件队列中删除事件
				(*ev->ev_callback)(ev->ev_fd, EV_READ,
						   ev->ev_arg);
					//调用事件注册的处理函数
			} else if (ev->ev_fd > maxfd)	//否则找最大描述符
				maxfd = ev->ev_fd;

			ev = old;
		}

		for (ev = TAILQ_FIRST(&writequeue); ev;) {
			old = TAILQ_NEXT(ev, ev_read_next);
			if (FD_ISSET(ev->ev_fd, event_writeset)) {
				event_del(ev);
				(*ev->ev_callback)(ev->ev_fd, EV_WRITE,
						   ev->ev_arg);
			} else if (ev->ev_fd > maxfd)
				maxfd = ev->ev_fd;

			ev = old;
		}
		event_inloop = 0;

		for (ev = TAILQ_FIRST(&addqueue); ev;
		     ev = TAILQ_FIRST(&addqueue)) {
			TAILQ_REMOVE(&addqueue, ev, ev_add_next);
			ev->ev_flags &= ~EVLIST_ADD;

			event_add_post(ev);

			if (ev->ev_fd > maxfd)
				maxfd = ev->ev_fd;
		}

		if (events_recalc(maxfd) == -1)
			return (-1);

		timeout_process();
	}

	return (0);
}

在这个主循环函数中,它调用了

events_recalc()这个函数,那么这个函数是干嘛的呢?
<pre name="code" class="cpp">/*
	分配事件集fd_set大小的函数:int event_recalc(int max)
	功能描述:根据,最大事件描述符,分配对应大小的事件集,在unix中,fd_set是通过对应位表示事件描述符的,所以,事件集的大小要足够大
			  参数为最大描述符,根据所传入的参数,分配最大fd_set空间
			  如果,传入的参数为0,函数会自己遍历读写队列,找到最大描述符,分配fd_set空间

	函数实现:1.将传入的参数,赋值给存储最大描述符的全局变量event_fds
			  2.判断最大描述符是否为0,是就遍历读写队列,找到队列中最大的描述符
			  3.计算,表示最大描述符,所需要的fd_set字节数
			  4.全局变量event_fdsz存储当前fd_set的字节大小,将最新计算出的fd_set大小和event_fdsz进行比较
				如果,最新计算出的所需大小大于当前fd_set的大小,就重新分配读写集合的空间
				更新全局变量event_fds,event_fdsz,event_readset,event_writeset的值
 */

int
events_recalc(int max)
{
	//读写,描述符·集合
	fd_set *readset, *writeset;
	//描述事件的结构体
	struct event *ev;
	int fdsz;

	//最大文件描述符在描述符集合中
	event_fds = max;

	//如果最大传入描述符为0
	if (!event_fds) {
		//在写队列中遍历找最大描述符
		TAILQ_FOREACH(ev, &writequeue, ev_write_next)
			if (ev->ev_fd > event_fds)
				event_fds = ev->ev_fd;
		//再去遍历读队列
		TAILQ_FOREACH(ev, &readqueue, ev_read_next)
			if (ev->ev_fd > event_fds)
				event_fds = ev->ev_fd;
	//最后event_fds中是最大描述符
	}

	//得到fd_set占字节数
	fdsz = howmany(event_fds + 1, NFDBITS) * sizeof(fd_mask);
	if (fdsz > event_fdsz) {
		if ((readset = realloc(event_readset, fdsz)) == NULL) {
			log_error("malloc");
			return (-1);
		}

		if ((writeset = realloc(event_writeset, fdsz)) == NULL) {
			log_error("malloc");
			free(readset);
			return (-1);
		}

		memset(readset + event_fdsz, 0, fdsz - event_fdsz);
		memset(writeset + event_fdsz, 0, fdsz - event_fdsz);

		event_readset = readset;
		event_writeset = writeset;
		event_fdsz = fdsz;
	}

	return (0);
}

/*

初始化事件:void event_set(struct event *ev, int fd, short events,void (*callback)(int, short, void *), void *arg)

功能描述:设置所传入的事件结构体的属性,完成对事件的初始化

函数实现:1.设置事件的回调函数

2.设置事件的参数ev_arg,它是回调函数的第三个参数

3.设置事件的参数ev_fd,它是回调函数的第一个参数

4.设置事件的参数ev_events,它是回调的第二个参数,也代表事件的类型(读、写、超时)

*/

void

event_set(struct event *ev, int fd, short events,

void (*callback)(int, short, void *), void *arg)

{

ev->ev_callback = callback;

ev->ev_arg = arg;

ev->ev_fd = fd;

ev->ev_events = events;

ev->ev_flags = EVLIST_INIT;

}


/*
<span style="white-space:pre">	</span>将事件添加到对应的读或者写队列:void event_add(struct event*ev,struct timeval* tv)
<span style="white-space:pre">	</span>功能描述:将事件添加到对应的读写队列中
<span style="white-space:pre">	</span>函数实现:1.如果阻塞等待时间不为空
<span style="white-space:pre">				</span>(1).得到当前时间
<span style="white-space:pre">				</span>(2).将当前时间与阻塞时间相加得到超时时间
<span style="white-space:pre">				</span>(3).判断事件是否在超时队列中
<span style="white-space:pre">					</span>如果在超时队列中,将事件从队列中移出
<span style="white-space:pre">				</span>(4).在超时队列中寻找合适的位置(超时队列,将超时时间从小到大排列)
<span style="white-space:pre">					</span>如果找到合适的位置,将其插入到该位置
<span style="white-space:pre">					</span>如果没有找到,将其从队尾插入
<span style="white-space:pre">				</span>(5).将事件在超时队列的标记置位
<span style="white-space:pre">			</span>  2.如果事件正在处理中
<span style="white-space:pre">				</span>(1).通过检测标记位判断当前被插入的事件是否之前就已经在add队列中
<span style="white-space:pre">					</span>如果已经在队列中,直接返回
<span style="white-space:pre">					</span>如果没有,将其加入add队列中,并将其标记置位
<span style="white-space:pre">			</span>  3.否则
<span style="white-space:pre">				</span>(1).调用event_add_post()将其加入到对应的读写队列中
*/
void
event_add(struct event *ev, struct timeval *tv)
{
<span style="white-space:pre">	</span>LOG_DBG((LOG_MISC, 55,
<span style="white-space:pre">		</span> "event_add: event: %p, %s%s%scall %p",
<span style="white-space:pre">		</span> ev,
<span style="white-space:pre">		</span> ev->ev_events & EV_READ ? "EV_READ " : " ",
<span style="white-space:pre">		</span> ev->ev_events & EV_WRITE ? "EV_WRITE " : " ",
<span style="white-space:pre">		</span> tv ? "EV_TIMEOUT " : " ",
<span style="white-space:pre">		</span> ev->ev_callback));
<span style="white-space:pre">		</span> 
<span style="white-space:pre">	</span>//如果等待时间不为空
<span style="white-space:pre">	</span>if (tv != NULL) {
<span style="white-space:pre">		</span>struct timeval now;
<span style="white-space:pre">		</span>struct event *tmp;
<span style="white-space:pre">		</span>
<span style="white-space:pre">		</span>//1.得到当前时间
<span style="white-space:pre">		</span>gettimeofday(&now, NULL);
<span style="white-space:pre">		</span>//2.将当前时间与阻塞等待时间相加,得到超时时间
<span style="white-space:pre">		</span>timeradd(&now, tv, &ev->ev_timeout);

<span style="white-space:pre">		</span>LOG_DBG((LOG_MISC, 55,
<span style="white-space:pre">			</span> "event_add: timeout in %d seconds, call %p",
<span style="white-space:pre">			</span> tv->tv_sec, ev->ev_callback));
<span style="white-space:pre">		</span>//如果有超时队列标记(即之前在队列中存在)
<span style="white-space:pre">		</span>if (ev->ev_flags & EVLIST_TIMEOUT)
<span style="white-space:pre">		</span>//将事件从时间队列移出
<span style="white-space:pre">			</span>TAILQ_REMOVE(&timequeue, ev, ev_timeout_next);

<span style="white-space:pre">		</span>/* Insert in right temporal order */
<span style="white-space:pre">		</span>//再次将时间事件找到正确的队列位置插入
<span style="white-space:pre">		</span>for (tmp = TAILQ_FIRST(&timequeue); tmp;
<span style="white-space:pre">		</span>     tmp = TAILQ_NEXT(tmp, ev_timeout_next)) {
<span style="white-space:pre">		</span>     if (timercmp(&ev->ev_timeout, &tmp->ev_timeout, <=))
<span style="white-space:pre">			</span>     break;
<span style="white-space:pre">		</span>}

<span style="white-space:pre">		</span>if (tmp)
<span style="white-space:pre">			</span>TAILQ_INSERT_BEFORE(tmp, ev, ev_timeout_next);
<span style="white-space:pre">		</span>else
<span style="white-space:pre">			</span>TAILQ_INSERT_TAIL(&timequeue, ev, ev_timeout_next);
<span style="white-space:pre">		</span>//再将时间队列标记置位
<span style="white-space:pre">		</span>ev->ev_flags |= EVLIST_TIMEOUT;
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>//如果事件正在循环中,判断一下被插入的事件是否是从添加等待队列中取出来的,如果是,直接返回,不是,将它添加到等待队列返回
<span style="white-space:pre">	</span>if (event_inloop) {
<span style="white-space:pre">		</span>/* We are in the event loop right now, we have to
<span style="white-space:pre">		</span> * postpone the change until later.
<span style="white-space:pre">		</span> */
<span style="white-space:pre">		</span>if (ev->ev_flags & EVLIST_ADD)
<span style="white-space:pre">			</span>return;

<span style="white-space:pre">		</span>TAILQ_INSERT_TAIL(&addqueue, ev, ev_add_next);
<span style="white-space:pre">		</span>ev->ev_flags |= EVLIST_ADD;
<span style="white-space:pre">	</span>} else
<span style="white-space:pre">		</span>event_add_post(ev);
}
/*
<span style="white-space:pre">	</span>将事件添加到对应的读或者写队列
<span style="white-space:pre">	</span>功能描述:通过判断事件的ev_events标记将其放入对应的队列
<span style="white-space:pre">	</span>函数实现:1.判断事件是读事件,并且事件没有在read队列中,将其添加到读队列,读队列标记置位
<span style="white-space:pre">			</span>  2.判断事件是写事件,并且事件没有在write队列中,将其添加到写队列,写队列标记置位
*/
void
event_add_post(struct event *ev)
{
<span style="white-space:pre">	</span>//如果,事件是读事件,并且没有在读队列中,就添加到读队列
<span style="white-space:pre">	</span>if ((ev->ev_events & EV_READ) && !(ev->ev_flags & EVLIST_READ)) {
<span style="white-space:pre">		</span>TAILQ_INSERT_TAIL(&readqueue, ev, ev_read_next);
<span style="white-space:pre">		</span>
<span style="white-space:pre">		</span>ev->ev_flags |= EVLIST_READ;
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>//如果,事件为写事件,并且没有在写队列,就添加到写队列
<span style="white-space:pre">	</span>if ((ev->ev_events & EV_WRITE) && !(ev->ev_flags & EVLIST_WRITE)) {
<span style="white-space:pre">		</span>TAILQ_INSERT_TAIL(&writequeue, ev, ev_write_next);
<span style="white-space:pre">		</span>
<span style="white-space:pre">		</span>ev->ev_flags |= EVLIST_WRITE;
<span style="white-space:pre">	</span>}
}
/*
<span style="white-space:pre">	</span>删除事件
<span style="white-space:pre">	</span>功能描述:把事件从所在的队列中清除
<span style="white-space:pre">	</span>函数实现:1.判断事件在读或者写或者添加或者超时队列中,将其从对应队列中删除,将其flag位置0
*/
void
event_del(struct event *ev)
{
<span style="white-space:pre">	</span>LOG_DBG((LOG_MISC, 80, "event_del: %p, callback %p",
<span style="white-space:pre">		</span> ev, ev->ev_callback));

<span style="white-space:pre">	</span>if (ev->ev_flags & EVLIST_ADD) {
<span style="white-space:pre">		</span>TAILQ_REMOVE(&addqueue, ev, ev_add_next);

<span style="white-space:pre">		</span>ev->ev_flags &= ~EVLIST_ADD;
<span style="white-space:pre">	</span>}

<span style="white-space:pre">	</span>if (ev->ev_flags & EVLIST_TIMEOUT) {
<span style="white-space:pre">		</span>TAILQ_REMOVE(&timequeue, ev, ev_timeout_next);

<span style="white-space:pre">		</span>ev->ev_flags &= ~EVLIST_TIMEOUT;
<span style="white-space:pre">	</span>}

<span style="white-space:pre">	</span>if (ev->ev_flags & EVLIST_READ) {
<span style="white-space:pre">		</span>TAILQ_REMOVE(&readqueue, ev, ev_read_next);

<span style="white-space:pre">		</span>ev->ev_flags &= ~EVLIST_READ;
<span style="white-space:pre">	</span>}

<span style="white-space:pre">	</span>if (ev->ev_flags & EVLIST_WRITE) {
<span style="white-space:pre">		</span>TAILQ_REMOVE(&writequeue, ev, ev_write_next);

<span style="white-space:pre">		</span>ev->ev_flags &= ~EVLIST_WRITE;
<span style="white-space:pre">	</span>}
}

/*
<span style="white-space:pre">	</span>获得新的阻塞时间
<span style="white-space:pre">	</span>功能描述:获得新的阻塞时间
<span style="white-space:pre">	</span>函数实现:1.如果超时队列没有元素,返回默认超时时间
<span style="white-space:pre">			</span>  2.如果有事件
<span style="white-space:pre">				</span>(1).得到当前时间
<span style="white-space:pre">				</span>(2).和队头事件的超时时间比较,如果已经超时,说明出现问题,直接清空超时时间,不阻塞
<span style="white-space:pre">				</span>(3).没超时,就将剩余的阻塞时间计算出来,作为新的阻塞时间
*/
int
timeout_next(struct timeval *tv)
{
<span style="white-space:pre">	</span>//当前时间
<span style="white-space:pre">	</span>struct timeval now;
<span style="white-space:pre">	</span>//指向事件的指针
<span style="white-space:pre">	</span>struct event *ev;
<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>//定时器队头事件指针是否为空
<span style="white-space:pre">	</span>if ((ev = TAILQ_FIRST(&timequeue)) == NULL) {
<span style="white-space:pre">		</span>//清空时间
<span style="white-space:pre">		</span>timerclear(tv);
<span style="white-space:pre">		</span>//定时默认5,返回
<span style="white-space:pre">		</span>tv->tv_sec = TIMEOUT_DEFAULT;
<span style="white-space:pre">		</span>return (0);
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>//得到当前时间
<span style="white-space:pre">	</span>if (gettimeofday(&now, NULL) == -1)
<span style="white-space:pre">		</span>return (-1);
<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>//和定时器队头事件定义的超时时间比较
<span style="white-space:pre">	</span>if (timercmp(&ev->ev_timeout, &now, <=)) {
<span style="white-space:pre">		</span>//不超时,清空结构
<span style="white-space:pre">		</span>timerclear(tv);
<span style="white-space:pre">		</span>return (0);
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>//定时器事件超时出的时间,更新到tv中
<span style="white-space:pre">	</span>timersub(&ev->ev_timeout, &now, tv);

<span style="white-space:pre">	</span>LOG_DBG((LOG_MISC, 60, "timeout_next: in %d seconds", tv->tv_sec));
<span style="white-space:pre">	</span>return (0);
}

/*
<span style="white-space:pre">	</span>超时后的处理
<span style="white-space:pre">	</span>功能描述:将超时队列中所有超时的事件从队列中移出,并且执行其对应的回调
<span style="white-space:pre">	</span>函数实现:1.获得当前时间
<span style="white-space:pre">			</span>  2.只要超时队列不为空,进入到队列中
<span style="white-space:pre">				</span>(1).从队头获得的超时时间比当前时间大,说明整个队列中都没有超时,跳出循环
<span style="white-space:pre">				</span>(2).否则,将队头从队列中移出,将队头事件的超时队列标记位置0,调用对应的超时回调处理,继续循环
*/
void
timeout_process(void)
{
<span style="white-space:pre">	</span>struct timeval now;
<span style="white-space:pre">	</span>struct event *ev;

<span style="white-space:pre">	</span>gettimeofday(&now, NULL);

<span style="white-space:pre">	</span>while ((ev = TAILQ_FIRST(&timequeue)) != NULL) {
<span style="white-space:pre">		</span>if (timercmp(&ev->ev_timeout, &now, >))
<span style="white-space:pre">			</span>break;

<span style="white-space:pre">		</span>TAILQ_REMOVE(&timequeue, ev, ev_timeout_next);
<span style="white-space:pre">		</span>ev->ev_flags &= ~EVLIST_TIMEOUT;

<span style="white-space:pre">		</span>LOG_DBG((LOG_MISC, 60, "timeout_process: call %p",
<span style="white-space:pre">			</span> ev->ev_callback));
<span style="white-space:pre">		</span>(*ev->ev_callback)(ev->ev_fd, EV_TIMEOUT, ev->ev_arg);
<span style="white-space:pre">	</span>}
}
时间: 2024-10-14 15:47:56

libevent源码学习研究(libevent-0.1)的相关文章

libevent 源码学习二 —— reactor 模式

1. 简介:Reactor : 反应堆模型, 一种事件驱动方式. 2. 优点 a 响应快,不必为单个同步时间所阻塞.(Reactor 本身是同步的) b 编程相对简单,可以最大程度的避免复杂的多线程和同步问题,避免了多线程/进程的切换开销 c 可扩展性, 可以方便的通过增加Reactor实例个数来充分利用CPU资源 d 可复用性, reactor框架本身与具体事件处理逻辑无关,具有高可复用性 3. 框架 a 事件源:Linux上是文件描述符, windows上是Socket或者Handle.统称

libevent 源码学习笔记

struct event_base { const struct eventop *evsel; void *evbase; int event_count; /* counts number of total events */ int event_count_active; /* counts number of active events */ int event_gotterm; /* Set to terminate loop */ /* active event management

libevent源码安装及Linux自动编译功能总结

这几天在阅读libevent源码,发现参考资料是基于libevent-2.1的版本,所以就去官网下载了2.1的版本: http://libevent.org/ (其实是在git下载的:https://github.com/libevent/libevent),版本应该是libevent-2.1.so.5.0.0 下载下来,发现目录里面没有常见的configure, Makefile等文件,搜索之后,了解到需要使用autoconf, automake等工具进行处理. 首先针对automake等工具

libevent源码深度剖析三

libevent源码深度剖析三 --libevent基本使用场景和事件流程张亮 1 前言 学习源代码该从哪里入手?我觉得从程序的基本使用场景和代码的整体处理流程入手是个不错的方法,至少从个人的经验上讲,用此方法分析libevent是比较有效的. 2 基本应用场景 基本应用场景也是使用libevnet的基本流程,下面来考虑一个最简单的场景,使用livevent设置定时器,应用程序只需要执行下面几个简单的步骤即可. 1)首先初始化libevent库,并保存返回的指针struct event_base

Libevent源码分析-timer和signal处理

timer处理 Signal处理 timerfd和signalfd timerfd signalfd timer处理 在Libevent源码分析-event处理流程中,使用了定时器,来看一下源码: evtimer_set(&ev, time_cb, NULL);//设置定时器事件 其中evtimer_set是个宏定义 #define evtimer_set(ev, cb, arg) event_set((ev), -1, 0, (cb), (arg)) //event_set原型 void ev

libevent源码分析-event

event结构 event相关接口 Libevent对event的管理 event结构 event是Reactor模式中的最重要的组件.它包含了了一个句柄fd,并设置监听这个句柄上的哪些事件(读/写等),设置了对应的函数指针,在事件到来时,回调函数指针来处理事件. 先看一下event的结构.它位于include/event2/event_struct.h中 struct event { TAILQ_ENTRY(event) ev_active_next; TAILQ_ENTRY(event) e

libevent 源码深度剖析十三

libevent 源码深度剖析十三 -- libevent 信号处理注意点 前面讲到了 libevent 实现多线程的方法,然而在多线程的环境中注册信号事件,还是有一些情况需要小心处理,那就是不能在多个 libevent 实例上注册信号事件.依然冠名追加到 libevent 系列. 以 2 个线程为例,做简单的场景分析. 1 首先是创建并初始化线程 1 的 libevent 实例 base1 ,线程 1 的 libevent 实例 base2 : 2 在 base1 上注册 SIGALRM 信号

libevent源码深度剖析二

libevent源码深度剖析二 --Reactor模式 张亮 前面讲到,整个libevent本身就是一个Reactor,因此本节将专门对Reactor模式进行必要的介绍,并列出libevnet中的几个重要组件和Reactor的对应关系,在后面的章节中可能还会提到本节介绍的基本概念. 1 Reactor的事件处理机制 首先来回想一下普通函数调用的机制:程序调用某函数?函数执行,程序等待?函数将结果和控制权返回给程序?程序继续处理. Reactor释义"反应堆",是一种事件驱动机制.和普通

libevent源码深度剖析十一

libevent源码深度剖析十一 --时间管理张亮 为了支持定时器,Libevent必须和系统时间打交道,这一部分的内容也比较简单,主要涉及到时间的加减辅助函数.时间缓存.时间校正和定时器堆的时间值调整等.下面就结合源代码来分析一下. 1 初始化检测 Libevent在初始化时会检测系统时间的类型,通过调用函数detect_monotonic()完成,它通过调用clock_gettime()来检测系统是否支持monotonic时钟类型: [cpp] view plaincopy static v