Dispatch类似于一个消息泵,在一个死循环中,不停地检查IO的状态(可以想像成不断从消息队列中读取消息),将状态的改变变成事件,再进行事件的响应。

主要代码如下:

[event.c]

int

event_base_loop(struct event_base *base, int flags)

{

const struct eventop *evsel = base->evsel;

...

done = 0;

base->event_gotterm = base->event_break = 0;

while (!done) {

base->event_continue = 0;

/* Terminate the loop if we have been asked to */

if (base->event_gotterm) {

break;

}

if (base->event_break) {

break;

}

...

/// 在这里调用底层的 dispatch

res = evsel->dispatch(base, tv_p);

...

if (N_ACTIVE_CALLBACKS(base)) {

/// 处理和响应事件

int n = event_process_active(base);

if ((flags & EVLOOP_ONCE)

&& N_ACTIVE_CALLBACKS(base) == 0

&& n != 0)

done = 1;

} else if (flags & EVLOOP_NONBLOCK)

done = 1;

}

done:

...

return (retval);

}

如果不是一次性事件,由while (!done)是一个死循环。这样可以反复地调用底层的dispatch去获取fd的状态. 在dispatch之后,又调用了event_process_active这个重要的函数。后面会讲到,它的作用是调用event绑定的回调函数。

select_dispatch

其主工作是调用了select函数,然后调用 evmap_io_active 触发事件。这个函数的实现有一个小技巧值得学习就是如何对随时变化的集合进行操作。

static int select_dispatch(struct event_base *base, struct timeval *tv)

{

int res=0, i, j, nfds;

struct selectop *sop = base->evbase;

///...

/// 主要工作是调整 event_XXXset_out 与 event_XXXset_in, select()基于前者,而对set的修改基于后者,在select之前有必要做一次同步

memcpy(sop->event_readset_out, sop->event_readset_in,

sop->event_fdsz);

memcpy(sop->event_writeset_out, sop->event_writeset_in,

sop->event_fdsz);

nfds = sop->event_fds+1;

EVBASE_RELEASE_LOCK(base, th_base_lock);

res = select(nfds, sop->event_readset_out,

sop->event_writeset_out, NULL, tv);

EVBASE_ACQUIRE_LOCK(base, th_base_lock);

...

i = random() % nfds;

for (j = 0; j < nfds; ++j) {

if (++i >= nfds)

i = 0;

res = 0;

if (FD_ISSET(i, sop->event_readset_out))

res |= EV_READ;

if (FD_ISSET(i, sop->event_writeset_out))

res |= EV_WRITE;

if (res == 0)

continue;

evmap_io_active(base, i, res);

}

check_selectop(sop);

return (0);

}

上面的代码中,先调用select获取fd的状态。注意一次并非返回一个fd,而操作两个fd的列表:读的fd列表,写的fd列表。接下来对select之后的队列进行操作,如果有读、写事件,则调用evmap_io_active在一个io map中登记,实际是将事件放到active队列中。此时只是登记,还没有调起事件对应的回调函数。

event_process_active

接下来分析如何调用事件对应的回调函数。回到event_base_dispatch(event_base_loop),从调用底层dispatch之后继续分析。到了最后调用了event_process_active。这个函数就是处理就绪队列的,就是它的内部实现调用了event关联的回调函数。event_process_active的处理流程是这样的:

event_process_active -> event_process_active_single_queue->(*ev->callback)(...)

[event.c]

static int event_process_active(struct event_base *base)

{

/* Caller must hold th_base_lock */

struct event_list *activeq = NULL;

int i, c = 0;

for (i = 0; i < base->nactivequeues; ++i) {

if (TAILQ_FIRST(&base->activequeues[i]) != NULL) {

base->event_running_priority = i;

activeq = &base->activequeues[i];

c = event_process_active_single_queue(base, activeq);

if (c < 0) {

base->event_running_priority = -1;

return -1;

} else if (c > 0)

break; /* Processed a real event; do not

* consider lower-priority events */

/* If we get here, all of the events we processed

* were internal.  Continue. */

}

}

event_process_deferred_callbacks(&base->defer_queue,&base->event_break);

base->event_running_priority = -1;

return c;

}

遍历activequeues,对每个项(一个队列),处理此项。

static int

event_process_active_single_queue(struct event_base *base,

struct event_list *activeq)

{

struct event *ev;

int count = 0;

EVUTIL_ASSERT(activeq != NULL);

for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) {

if (ev->ev_events & EV_PERSIST)

event_queue_remove(base, ev, EVLIST_ACTIVE);

else

event_del_internal(ev);

switch (ev->ev_closure) {

case EV_CLOSURE_SIGNAL:

event_signal_closure(base, ev);

break;

case EV_CLOSURE_PERSIST:

event_persist_closure(base, ev);

break;

default:

case EV_CLOSURE_NONE:

EVBASE_RELEASE_LOCK(base, th_base_lock);

(*ev->ev_callback)(

ev->ev_fd, ev->ev_res, ev->ev_arg);

break;

}

...

if (base->event_break)

return -1;

if (base->event_continue)

break;

}

return count;

}

libevent2源码分析之四:libevent2的消息泵的更多相关文章

  1. zookeeper源码分析之四服务端(单机)处理请求流程

    上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...

  2. RocketMQ源码分析之RocketMQ事务消息实现原理上篇(二阶段提交)

    在阅读本文前,若您对RocketMQ技术感兴趣,请加入 RocketMQ技术交流群 根据上文的描述,发送事务消息的入口为: TransactionMQProducer#sendMessageInTra ...

  3. 【TencentOS tiny】深度源码分析(4)——消息队列

    消息队列 在前一篇文章中[TencentOS tiny学习]源码分析(3)--队列 我们描述了TencentOS tiny的队列实现,同时也点出了TencentOS tiny的队列是依赖于消息队列的, ...

  4. Spark源码分析之四:Stage提交

    各位看官,上一篇<Spark源码分析之Stage划分>详细讲述了Spark中Stage的划分,下面,我们进入第三个阶段--Stage提交. Stage提交阶段的主要目的就一个,就是将每个S ...

  5. spark 源码分析之四 -- TaskScheduler的创建和启动过程

    在 spark 源码分析之二 -- SparkContext 的初始化过程 中,第 14 步 和 16 步分别描述了 TaskScheduler的 初始化 和 启动过程. 话分两头,先说 TaskSc ...

  6. libevent2源码分析之五:关键的调用链

    用一个调用链来表示函数调用的流程,看起来更直观.根据上面的分析,总结了一些重要的调用链. 初始化 event_base_new event_base_new_with_config min_heap_ ...

  7. libevent2源码分析之三:信号的初始化流程

    libevent2对信号的响应也进行了封装,使之与socket操作一样对外提供统一的接口.这里的信号一般指linux的信号.由于信号与socket相关的编程接口有较大的不同,因此在内部实现也有一些区别 ...

  8. libevent2源码分析之一:前言

    event的本质 libevent2中的event的本质是什么?只要是非同步阻塞的运行方式,肯定遵循事件的订阅-发布模型.通过event_new的函数原型可以理解,一个event即代表一次订阅,建立起 ...

  9. libevent2源码分析之二:初始化流程

    本文并不很详细地分析初始化的各个细节,而重点分析如何将底层操作关联到event_base的相关字段.初始化工作主要是针对event_base的.libevent2支持多种底层实现,有epoll, se ...

随机推荐

  1. python的class的__str__和__repr__(转)

    本文参考自: https://stackoverflow.com/questions/18393701/the-difference-between-str-and-repr?noredirect=1 ...

  2. [转]CreateDIBitmap与CreateDIBSection

    首先明确最主要区别:CreateDIBitmap创建的是设备相关位图句柄 - HBITMAP.                               CreateDIBSection创建的是设备 ...

  3. 安装smartmontool报错:libc6-dev : 破坏:

    https://blog.csdn.net/weixin_38705903/article/details/81947717

  4. 胖AP与瘦AP区别

    一.胖AP组网方案 1.漫游问题 用户从一个胖AP的覆盖区域走到另一个胖AP的覆盖区域,会重新连接信号强的一个胖AP,重新进行认证,重新获取IP地址,存在断网现象: 2.无法保证WLAN的安全性 为了 ...

  5. HDU 2102 A计划【三维BFS】

    A计划 Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submissio ...

  6. HDU 2562 奇偶位互换(字符串,水)

      奇偶位互换 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Sub ...

  7. Linux CentOS下Python+robot framework环境搭建

    转载自:http://blog.sina.com.cn/s/blog_13cc013b50102vof1.html 操作系统环境:CentOS 6.5-x86_64 下载地址:http://www.c ...

  8. AxureRP7超强部件库打包下载

    摘要: 很多刚刚开始学习Axure的朋友都喜欢到网上搜罗各种部件库(组件库)widgets library ,但是网络中真正实用的并且适合你使用的少之又少,最好的办法就是自己制作适合自己工作内容的部件 ...

  9. [BZOJ3684]大朋友和多叉树

    设答案为$f_s$,它的生成函数为$\begin{align*}F(x)=\sum\limits_{i=0}^\infty f_ix^i\end{align*}$,则我们有$\begin{align* ...

  10. 【动态规划】bzoj1649 [Usaco2006 Dec]Cow Roller Coaster

    很像背包. 这种在一个数轴上进行操作的题常常需要对区间排序. f[i][j]表示距离到i时,花费为j时的权值之和. f[x[i]+l[i]][j+c[i]]=max{f[x[i]][j]+w[i]}( ...