接下来就是将已经初始化的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. 详解基于朴素贝叶斯的情感分析及 Python 实现

    相对于「 基于词典的分析 」,「 基于机器学习 」的就不需要大量标注的词典,但是需要大量标记的数据,比如: 还是下面这句话,如果它的标签是: 服务质量 - 中 (共有三个级别,好.中.差) ╮(╯-╰ ...

  2. Leetcode 371.两整数之和 By Python

    不使用运算符 + 和 - ,计算两整数 a .b 之和. 示例 1: 输入: a = 1, b = 2 输出: 3 示例 2: 输入: a = -2, b = 3 输出: 1 思路 比如\(5+6=1 ...

  3. 20 Zabbix 利用Scripts栏目对Hosts远程执行命令

    点击返回:自学Zabbix之路 点击返回:自学Zabbix4.0之路 点击返回:自学zabbix集锦 20 Zabbix 利用Scripts栏目对Hosts远程执行命令 在Monitoring板块中, ...

  4. 自学Linux Shell8.1-linux文件系统概述及操作

    点击返回 自学Linux命令行与Shell脚本之路 8.1-linux文件系统概述及操作 1. linux支持的文件系统 Windows常用的分区格式有三种,分别是FAT16.FAT32.NTFS格式 ...

  5. 自学Linux Shell11.3-使用变量

    点击返回 自学Linux命令行与Shell脚本之路 11.3-使用变量 Shell脚本的执行通常可以采用以下几种方式: 1):bash script-name或sh script-name(推荐使用) ...

  6. 百度地图infoWindow圆角处理

    最近的一个项目用到了百度地图API里边的infoWindow弹框,但是百度自带的infoWindow弹框是个直角的矩形框,显示过于难看,于是有了将该框改为圆角的想法,但是API本身不支持样式的设置,所 ...

  7. Sublime Text3—Project(项目管理)

    摘要 Project 可以理解为项目.工程或者站点,以下称项目.使用项目管理的好处是:不用将所有文件都放到同一个根目录,可以将相关但不同路径的文件组成一个Project,每个项目都是独立的,文件的状态 ...

  8. 走进HTTP协议之二 基本HTTP机制

    本系列第一节,我们回顾了与HTTP协议有关的基本术语和概念,本文将分析HTTP协议的基本原理与机制 HTTP协议的用途 HTTP协议用于客户端与服务器之间的通信,在通信线路两端,必定一端是客户端,另一 ...

  9. Android Support Library 是什么?

    这两天刚开始学习安卓,这里记录下这两天遇到的一些小问题. 首先先贴一个安卓 API 等级. 官方地址:https://developer.android.com/about/dashboards/ ( ...

  10. 项目管理工具Maven的安装

    首先下载maven http://maven.apache.org/download.cgi 然后解压到不含中文和空格的目录,我直接解压到了D盘 然后添加环境变量: MAVEN_HOME:D:\apa ...