接下来就是将已经初始化的event注册到libevent的事件链表上,通过event_add()来实现,源码位于event.c中。

event_add()

这个函数主要完成了下面几件事:
1.将event注册到event_base的I/O多路复用要监听的事件中
2.将event注册到event_base的已注册事件链表中
3.如果传入了超时时间,则删除旧的超时时间,重新设置,并将event添加到event_base的小根堆中;
   如果没有传入超时时间,则不会添加到小根堆中。
只有步骤1成功,才会执行步骤2和3;否则什么都没做,直接返回,保证不会改变event的状态。
 
从中还可以看到,将event添加到已注册事件链表、添加到小根堆、从活跃事件链表移除、从小根堆中移除,都是通过两个函数完成的:event_queue_insert()、event_queue_remove()
int
event_add(struct event *ev, const struct timeval *tv)
{
struct event_base *base = ev->ev_base; //event所属的event_base
const struct eventop *evsel = base->evsel; //event_base的I/O多路复用机制
void *evbase = base->evbase; //event_base的I/O多路复用机制
int res = ;
//DEBUG log.h
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.
*/
//如果传入了超时时间并且event不再time小根堆上,则在小根堆上预留一个位置
//以保证如果后面有步骤失败,不会改变初始状态,保证是个原子操作
if (tv != NULL && !(ev->ev_flags & EVLIST_TIMEOUT)) {
if (min_heap_reserve(&base->timeheap, //min_heap.h
+ min_heap_size(&base->timeheap)) == -)
return (-); /* ENOMEM == errno */
}
//如果event不在已注册链表或活跃链表中,
//则调用evsel->add()注册event事件到I/O多路复用监听的事件上
if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) &&
!(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
res = evsel->add(evbase, ev); //将event注册到监听事件上
//注册监听事件成功,则将event注册到已注册事件链表上
if (res != -)
event_queue_insert(base, ev, EVLIST_INSERTED); //插入
}
/*
* we should change the timout state only if the previous event
* addition succeeded.
*/
//前面操作都成功情况下,才能执行下面步骤
//改变超时状态
if (res != - && tv != NULL) {
struct timeval now;
/*
* we already reserved memory above for the case where we
* are not replacing an exisiting timeout.
*/
//EVLIST_TIMEOUT表明event已在定时器堆中
//则删除旧的定时器
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添加超时时间
event_debug((
"event_add: timeout in %ld seconds, call %p",
tv->tv_sec, ev->ev_callback));
//将event插入到小根堆中
event_queue_insert(base, ev, EVLIST_TIMEOUT); //插入
}
return (res);
}

event_queue_insert()

该函数根据不同的输入队列,即不同的事件,在不同的队列中插入,并增加相应的事件计数,更新event状态;
EVLIST_INSERTED:在已注册事件链表event_base.eventqueue插入
EVLIST_ACTIVE:根据event优先级,在活跃事件链表event_base.activequeues[event.ev_pri]插入
EVLIST_TIMEOUT:在小根堆event_base.timeheap中插入
void
event_queue_insert(struct event_base *base, struct event *ev, int queue)
{
//如果event已经在活跃链表中,则返回;否则,出错
if (ev->ev_flags & queue) {
/* Double insertion is possible for active events */
if (queue & EVLIST_ACTIVE)
return;
event_errx(, "%s: %p(fd %d) already on queue %x", __func__,
ev, ev->ev_fd, queue);
}
if (~ev->ev_flags & EVLIST_INTERNAL)
base->event_count++; //增加注册事件数
ev->ev_flags |= queue; //改变event状态
switch (queue) { //根据不同的输入参数队列,选择在不同的事件集合中插入
case EVLIST_INSERTED: //I/O或Signal事件
TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next); //在已注册事件链表插入
break;
case EVLIST_ACTIVE: //活跃事件
base->event_count_active++; //增加活跃事件数
TAILQ_INSERT_TAIL(base->activequeues[ev->ev_pri], //在活跃事件链表插入
ev,ev_active_next);
break;
case EVLIST_TIMEOUT: { //定时器事件
min_heap_push(&base->timeheap, ev); //在小根堆插入
break;
}
default:
event_errx(, "%s: unknown queue %x", __func__, queue);
}
}

event_queue_remove()

和event_queue_insert()相对应,这个函数主要根据不同的输入参数,从不同的事件集合中删除事件。
void
event_queue_remove(struct event_base *base, struct event *ev, int queue)
{
if (!(ev->ev_flags & queue))
event_errx(, "%s: %p(fd %d) not on queue %x", __func__,
ev, ev->ev_fd, queue);
if (~ev->ev_flags & EVLIST_INTERNAL)
base->event_count--;
ev->ev_flags &= ~queue;
switch (queue) {
case EVLIST_INSERTED: //I/O、Signal事件
TAILQ_REMOVE(&base->eventqueue, ev, ev_next);
break;
case EVLIST_ACTIVE: //活跃事件
base->event_count_active--;
TAILQ_REMOVE(base->activequeues[ev->ev_pri],
ev, ev_active_next);
break;
case EVLIST_TIMEOUT: //定时器事件
min_heap_erase(&base->timeheap, ev);
break;
default:
event_errx(, "%s: unknown queue %x", __func__, queue);
}
}

event_del()

libevent还提供了event_del()这个函数,该函数从直接删除event事件,该函数就是主要通过调用event_queue_remove()函数完成删除的功能。
另外,该函数还将event从I/O多路复用监听的事件中删除。
int
event_del(struct event *ev)
{
struct event_base *base;
const struct eventop *evsel;
void *evbase;
event_debug(("event_del: %p, callback %p",
ev, ev->ev_callback));
/* An event without a base has not been added */
if (ev->ev_base == NULL)
return (-);
base = ev->ev_base;
evsel = base->evsel;
evbase = base->evbase;
assert(!(ev->ev_flags & ~EVLIST_ALL));
/* See if we are just active executing this event in a loop */
//计数清0
if (ev->ev_ncalls && ev->ev_pncalls) {
/* Abort loop */
*ev->ev_pncalls = ;
}
//根据event不同的状态,从相应的event集合中删除
if (ev->ev_flags & EVLIST_TIMEOUT)
event_queue_remove(base, ev, EVLIST_TIMEOUT);
if (ev->ev_flags & EVLIST_ACTIVE)
event_queue_remove(base, ev, EVLIST_ACTIVE);
if (ev->ev_flags & EVLIST_INSERTED) {
event_queue_remove(base, ev, EVLIST_INSERTED);
return (evsel->del(evbase, ev)); //从I/O多路复用监听的事件中删除
}
return ();
}
 

Libevent源码分析—event_add()的更多相关文章

  1. 【转】libevent源码分析

    libevent源码分析 转自:http://www.cnblogs.com/hustcat/archive/2010/08/31/1814022.html 这两天没事,看了一下Memcached和l ...

  2. Libevent源码分析 (1) hello-world

    Libevent源码分析 (1) hello-world ⑨月份接触了久闻大名的libevent,当时想读读源码,可是由于事情比较多一直没有时间,现在手头的东西基本告一段落了,我准备读读libeven ...

  3. libevent源码分析:event_add、event_del

    event_add.event_del两个函数分别是使event生效和失效的,下面就来看一下两个函数的实现. event_add int event_add(struct event *ev, con ...

  4. Libevent源码分析系列【转】

    转自:https://www.cnblogs.com/zxiner/p/6919021.html 1.使用libevent库     源码那么多,该怎么分析从哪分析呢?一个好的方法就是先用起来,会用了 ...

  5. Libevent源码分析系列

    1.使用libevent库     源码那么多,该怎么分析从哪分析呢?一个好的方法就是先用起来,会用了,然后去看底层相应的源码,这样比较有条理,自上向下掌握.下面用libevent库写个程序,每隔1秒 ...

  6. libevent源码分析

    这两天没事,看了一下Memcached和libevent的源码,做个小总结. 1.入门 1.1.概述Libevent是一个用于开发可扩展性网络服务器的基于事件驱动(event-driven)模型的网络 ...

  7. libevent源码分析一--io事件响应

    这篇文章将分析libevent如何组织io事件,如何捕捉事件的发生并进行相应的响应.这里不会详细分析event与event_base的细节,仅描述io事件如何存储与如何响应. 1.  select l ...

  8. Libevent源码分析—event_base_dispatch()

    我们知道libevent是一个Reactor模式的事件驱动的网络库.   到目前为止,我们已经看了核心的event和event_base结构体的源码,看了初始化这两个结构体的源码,看了注册event的 ...

  9. libevent源码分析二--timeout事件响应

    libevent不仅支持io事件,同时还支持timeout事件与signal事件,这篇文件将分析libevent是如何组织timeout事件以及如何响应timeout事件. 1.  min_heap ...

随机推荐

  1. 16 利用Zabbix完成windows监控

    点击返回:自学Zabbix之路 16 利用Zabbix完成windows监控 1.安装zabbix_agentd 1.1.下载zabbix_agentd监控客户端软件安装包(windows操作系统客户 ...

  2. Announcing WebKit SDL port

    转自:http://www.dorothybrowser.com/announcing-webkit-sdl-port/ 下载地址 https://gitorious.org/spiegel/webk ...

  3. Codeforces Round #548

    没打,简单补档 C.Edgy Trees 容斥,把黑边断掉数联通块,每个联通块贡献$siz^k$ #include<cstdio> #include<cstring> #inc ...

  4. A1012. The Best Rank

    To evaluate the performance of our first year CS majored students, we consider their grades of three ...

  5. A1032. Sharing

    To store English words, one method is to use linked lists and store a word letter by letter. To save ...

  6. es6/es7/es8常用新特性总结(超实用)

    本文标题有误导性,因为我其实想写node8的新特性,说实话一下子从node v1.x跳跃到node 8.x+ 真有点受宠若惊的感觉.一直觉得node 数组. 对象.序列等的处理没有python方便,因 ...

  7. calloc()的使用

    百度百科 calloc是一个ISO C函数 函数名: calloc 函数原型:void *calloc(size_t n, size_t size): 功 能: 在内存的动态存储区中分配n个长度为si ...

  8. cisco常用命令详解

    cisco常用命令详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.常用命令用法展示 1.命令行模式的来回切换 yinzhengjie>enable #从用户模式切换到 ...

  9. JavaSE学习总结(四)——Java面向对象十分钟入门

    面向对象编程(Object Oriented Programming,OOP)是一种计算机模拟人类的自然思维方式的编程架构技术,解决了传统结构化开发方法中客观世界描述工具与软件结构的不一致性问题.Ja ...

  10. Study 8 —— 数据类型(列表/list)

    列表是一个数据的集合,集合内可以放任何数据类型,可对集合进行方便的增删改查操作 1. 定义列表: 方法一: L1 = [] #定义空列表 L2 = ['A', 'B', 'C']  #存3个值,索引0 ...