本文并不很详细地分析初始化的各个细节,而重点分析如何将底层操作关联到event_base的相关字段。初始化工作主要是针对event_base的。libevent2支持多种底层实现,有epoll, select, iocp等。下面的工作主要是以熟悉的select作为底层实现,分析libevent2的工作机理。

event_base的结构片断如下:

struct event_base {

/** Function pointers and other data to describe this event_base's

* backend. */

/// 保存底层操作的抽象对象(实际上是IO操作)

const struct eventop *evsel;

/** Pointer to backend-specific data. */

/// 保存底层操作对象要操作的对象

void *evbase;

...

}

不管底层操作是 select 还是 epoll 还是其它。都被抽象成下面的几个操作: init, add, del, dispatch...

struct eventop {

const char *name;

void *(*init)(struct event_base *);

int (*add)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo);

int (*del)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo);

int (*dispatch)(struct event_base *, struct timeval *);

void (*dealloc)(struct event_base *);

int need_reinit;

enum event_method_feature features;

size_t fdinfo_len;

};

基于 select 操作是如何初始化 const struct eventop *evsel 这个变量的?

1. select.c 中定义了一个 eventop 类型的static变量。

const struct eventop selectops = {

"select",

select_init,

select_add,

select_del,

select_dispatch,

select_dealloc,

0, /* doesn't need reinit. */

EV_FEATURE_FDS,

0,

};

这样selectops的init就指向select_init, add指向select_add...

2. 将selectops注册到 eventops 这个数组中,作为底层操作的一个选项。数组中,位于前面的数据具有优先选择权。可以看到,select作为一个不被推荐的方式放到了倒数第二的位置。但这并不妨碍我们使用select这个熟悉的方式来分析libevent2的运行机制。

[event.c]

/* Array of backends in order of preference. */

static const struct eventop *eventops[] = {

#ifdef _EVENT_HAVE_EVENT_PORTS

&evportops,

#endif

#ifdef _EVENT_HAVE_WORKING_KQUEUE

&kqops,

#endif

#ifdef _EVENT_HAVE_EPOLL

&epollops,

#endif

#ifdef _EVENT_HAVE_DEVPOLL

&devpollops,

#endif

#ifdef _EVENT_HAVE_POLL

&pollops,

#endif

#ifdef _EVENT_HAVE_SELECT

&selectops,

#endif

#ifdef WIN32

&win32ops,

#endif

NULL

};

3. 那么在哪里将 eventops 给event_base.evsel 赋值呢?赋赋值操作的前面和后面做了些什么呢?

这是在创建event_base做的事情。且看 event_base_new_with_config函数的实现。

[event.c]

struct event_base * event_base_new_with_config(const struct event_config *cfg)

{

...

for (i = 0; eventops[i] && !base->evbase; i++) {

...

base->evsel = eventops[i];

base->evbase = base->evsel->init(base);

}

...

}

可见是将数组中的第1个有效的记录赋值给了 base->evsel, 作为底层的实现。同时调用了 init 函数,将返回的操作数据传递给了 base->evbase.

event_base_new() 的内部调用了 event_base_new_with_config.

再深入地跟踪一下 init 函数。看它做了些什么,返回了些什么。select 模型对应的 init 是 select_init.

[select.c]

static void *

select_init(struct event_base *base)

{

struct selectop *sop;

if (!(sop = mm_calloc(1, sizeof(struct selectop))))

return (NULL);

if (select_resize(sop, SELECT_ALLOC_SIZE(32 + 1))) {

select_free_selectop(sop);

return (NULL);

}

evsig_init(base);

return (sop);

}

struct selectop {

int event_fds;        /* Highest fd in fd set */

int event_fdsz;

int resize_out_sets;

fd_set *event_readset_in;

fd_set *event_writeset_in;

fd_set *event_readset_out;

fd_set *event_writeset_out;

};

可见初始化并返回了一个struct selectop类型的指针。一般的思路是通过 calloc(和malloc类似)申请一段内存,再调用evsig_init初始化信号的底层实现。那么调用 select_resize 干什么呢?看一下 struct selectop的结构,后面的四个指针指向的内存还没有初始化呢,select_resize就是初始化这些指针,让它指向一个fd_set的数组。数组的大小是多少字节呢?这个由宏 SELECT_ALLOC_SIZE(32 + 1) 计算。这个宏的参数 32 + 1 即为数组的长度,根据select的规则,32为有效的长度。这些描述表明,读取队列的初始长度是32.

libevent2源码分析之二:初始化流程的更多相关文章

  1. spark 源码分析之二十一 -- Task的执行流程

    引言 在上两篇文章 spark 源码分析之十九 -- DAG的生成和Stage的划分 和 spark 源码分析之二十 -- Stage的提交 中剖析了Spark的DAG的生成,Stage的划分以及St ...

  2. Kubernetes Deployment 源码分析(二)

    概述startDeploymentController 入口逻辑DeploymentController 对象DeploymentController 类型定义DeploymentController ...

  3. DataTable源码分析(二)

    DataTable源码分析(二) ===================== DataTable函数分析 ---------------- DataTable作为整个插件的入口,完成了整个表格的数据初 ...

  4. 一个普通的 Zepto 源码分析(二) - ajax 模块

    一个普通的 Zepto 源码分析(二) - ajax 模块 普通的路人,普通地瞧.分析时使用的是目前最新 1.2.0 版本. Zepto 可以由许多模块组成,默认包含的模块有 zepto 核心模块,以 ...

  5. Koa源码分析(二) -- co的实现

    Abstract 本系列是关于Koa框架的文章,目前关注版本是Koa v1.主要分为以下几个方面: Koa源码分析(一) -- generator Koa源码分析(二) -- co的实现 Koa源码分 ...

  6. Spring IOC 容器源码分析 - 余下的初始化工作

    1. 简介 本篇文章是"Spring IOC 容器源码分析"系列文章的最后一篇文章,本篇文章所分析的对象是 initializeBean 方法,该方法用于对已完成属性填充的 bea ...

  7. (转)linux内存源码分析 - 内存回收(整体流程)

    http://www.cnblogs.com/tolimit/p/5435068.html------------linux内存源码分析 - 内存回收(整体流程) 概述 当linux系统内存压力就大时 ...

  8. HDFS源码分析DataXceiver之整体流程

    在<HDFS源码分析之DataXceiverServer>一文中,我们了解到在DataNode中,有一个后台工作的线程DataXceiverServer.它被用于接收来自客户端或其他数据节 ...

  9. SDL2源码分析1:初始化(SDL_Init())

    ===================================================== SDL源码分析系列文章列表: SDL2源码分析1:初始化(SDL_Init()) SDL2源 ...

随机推荐

  1. <Linux性能调优指南>主要思路流程

    网上IBM很早放出的一本免费电子书, 十来年了,参考意义还是很大. 国内有翻译成中文在线阅读的版本. 见如下两个URL Linux Performance and Tuning Guidelines ...

  2. (六)MySQL数据操作DML

    (1)insert:插入数据 顺序插入数据 insert into 表名 values(值1,值2,值3); 指定字段插入数据 insert into 表名(字段1,字段2,字段3) values(值 ...

  3. CSS3实现鼠标经过图片时图片放大

    在鼠标经过图片时,图片被放大,而且还有个过渡的效果.... <!DOCTYPE html> <html> <head> <link href="cs ...

  4. 【ZOJ】3785 What day is that day? ——KMP 暴力打表找规律

    转自:http://www.cnblogs.com/kevince/p/3887827.html 首先声明一下,这里的规律指的是循环,即找到最小循环周期. 这么一说大家心里肯定有数了吧,“不就是nex ...

  5. Codeforces #442 Div2 E

    #442 Div2 E 题意 给你一棵树,每个结点有开关(0表示关闭,1表示开启),两种操作: 反转一棵子树所有开关 询问一棵子树有多少开关是开着的 分析 先 DFS 把树上的结点映射到区间上,然后就 ...

  6. Column 1 of table 'xxx' cannot be converted from type 'varchar(33)' to type 'varchar(11)'

    mysql主从同步失败.错误日志如下. Column 1 of table 'xxx' cannot be converted from type 'varchar(33)' to type 'var ...

  7. 【树链剖分】【函数式权值分块】bzoj1146 [CTSC2008]网络管理Network

    裸题,直接上.复杂度O(n*sqrt(n)*log(n)). //Num[i]表示树中的点i在函数式权值分块中对应的点 //Map[i]表示函数式权值分块中的点i在树中对应的点 #include< ...

  8. 【分块】bzoj2724 [Violet 6]蒲公英

    分块,离散化,预处理出: ①前i块中x出现的次数(差分): ②第i块到第j块中的众数是谁,出现了多少次. 询问的时候,对于整块的部分直接获得答案:对于零散的部分,暴力统计每个数出现的次数,加上差分的结 ...

  9. [JOISC2014]JOIOJI

    题目大意: 给你一串仅包含'J''O''I'的字符串,问满足三种字符出现次数相等的最大字串是多少? 思路: 用map存一下出现次数前缀和两两之差出现的最早位置,每次看一下当前的两两之差最早的出现位置是 ...

  10. 使用virtualenv为应用提供了隔离的Python运行环境

    在开发Python应用程序的时候,系统安装的Python3只有一个版本:3.4.所有第三方的包都会被pip安装到Python3的site-packages目录下. 如果我们要同时开发多个应用程序,那这 ...