libevent2源码分析之二:初始化流程
本文并不很详细地分析初始化的各个细节,而重点分析如何将底层操作关联到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源码分析之二:初始化流程的更多相关文章
- spark 源码分析之二十一 -- Task的执行流程
引言 在上两篇文章 spark 源码分析之十九 -- DAG的生成和Stage的划分 和 spark 源码分析之二十 -- Stage的提交 中剖析了Spark的DAG的生成,Stage的划分以及St ...
- Kubernetes Deployment 源码分析(二)
概述startDeploymentController 入口逻辑DeploymentController 对象DeploymentController 类型定义DeploymentController ...
- DataTable源码分析(二)
DataTable源码分析(二) ===================== DataTable函数分析 ---------------- DataTable作为整个插件的入口,完成了整个表格的数据初 ...
- 一个普通的 Zepto 源码分析(二) - ajax 模块
一个普通的 Zepto 源码分析(二) - ajax 模块 普通的路人,普通地瞧.分析时使用的是目前最新 1.2.0 版本. Zepto 可以由许多模块组成,默认包含的模块有 zepto 核心模块,以 ...
- Koa源码分析(二) -- co的实现
Abstract 本系列是关于Koa框架的文章,目前关注版本是Koa v1.主要分为以下几个方面: Koa源码分析(一) -- generator Koa源码分析(二) -- co的实现 Koa源码分 ...
- Spring IOC 容器源码分析 - 余下的初始化工作
1. 简介 本篇文章是"Spring IOC 容器源码分析"系列文章的最后一篇文章,本篇文章所分析的对象是 initializeBean 方法,该方法用于对已完成属性填充的 bea ...
- (转)linux内存源码分析 - 内存回收(整体流程)
http://www.cnblogs.com/tolimit/p/5435068.html------------linux内存源码分析 - 内存回收(整体流程) 概述 当linux系统内存压力就大时 ...
- HDFS源码分析DataXceiver之整体流程
在<HDFS源码分析之DataXceiverServer>一文中,我们了解到在DataNode中,有一个后台工作的线程DataXceiverServer.它被用于接收来自客户端或其他数据节 ...
- SDL2源码分析1:初始化(SDL_Init())
===================================================== SDL源码分析系列文章列表: SDL2源码分析1:初始化(SDL_Init()) SDL2源 ...
随机推荐
- MATLAB求函数零点与极值
1. roots函数 针对多项式求零点(详见MATLAB多项式及多项式拟合) 2. fzero函数 返回一元函数在某个区间内的的零点. x0 = fzero(@(x)x.^2-3* ...
- 使用 URLDecoder 和 URLEncoder 对统一认证中的http地址转义字符进行处理
import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; ...
- centeros7远程访问mysql5.7
先启动firewall防火墙: service firewalld start 打开3306端口: firewall-cmd --add-port=/tcp --permanent mysql授权ro ...
- 使用threadpool并发测试,报错HTTPConnectionPool Max retires exceeded
解决方法:和以下答案一致 https://blog.csdn.net/qq_21405949/article/details/79363084 场景: 在做爬虫项目或者是在发送网络请求的时候,一般都会 ...
- codeforces Round #440 A Search for Pretty Integers【hash/排序】
A. Search for Pretty Integers [题目链接]:http://codeforces.com/contest/872/problem/A time limit per test ...
- HDU 1686 Oulipo【kmp求子串出现的次数】
The French author Georges Perec (1936–1982) once wrote a book, La disparition, without the letter 'e ...
- Tarjan求LCA总结
Tarjan算法向上标记法:从x向上走到根节点,并标记所有经过的点从y向上走到根节点,当第一次遇到已标记的节点时,就找到了LCA(x, y)对于每个询问,向上标记法的时间复杂度最坏为O(n) 在深度遍 ...
- Exchange2010启用反垃圾邮件功能
今天邮箱服务器发现有大量发件人为空的邮件等待执行,也就是说空邮件堵塞了队列. 一般来说,空邮件就是别人发送垃圾邮件给你,你的服务上不存在这个收件人,那么系统会产生一封退信告诉你这封邮件已经被退.而ex ...
- IDEA的Maven项目找不到class
- 【Heap-Dijkstra】【分层图】bzoj2763 [JLOI2011]飞行路线
建立k+1张图, 在图与图之间,若在原图中x到y有边,就建立从 第i层的x 到 i+1层的y 建边,权值为0.代表一次免费机会. 由于一旦到了第i+1层的图里,则无法回到之前的层,所以免费最多只有k次 ...