在《libev源码解析——监视器(watcher)结构和组织形式》中介绍过,监视器分为[2,-2]区间5个等级的优先级。等级为2的监视器最高优,然后依次递减。不区分监视器类型和关联的文件描述符的值,权限高的要优先于权限低的执行。但是ANFD结构中的监视器链表无法满足高等级优先执行的特性。那么libev是如何解决这个问题的呢?(转载请指明出于breaksoftware的csdn博客)

anfds结构是以文件描述符作为索引的,其关心的是该描述符对应的事件是否发生。那我们关心不同等级执行顺序时,要以什么作为索引呢?那当然是等级值。libev也的确是这么做的

VAR (pendings, ANPENDING *pendings [NUMPRI])
VAR (pendingmax, int pendingmax [NUMPRI])
VAR (pendingcnt, int pendingcnt [NUMPRI])

NUMPRI是等级的个数,其定义是

#define NUMPRI (EV_MAXPRI - EV_MINPRI + 1)

pendings是一个具有5(2-(-2)+1))个元素的数组,不同等级和数组下标的对应关系通过下面这个宏来换算

# define ABSPRI(w) (((W)w)->priority - EV_MINPRI)

可见高等级的位于数组末尾,低等级的位于数组头部。即等级为2的pendings数组下标是4,而等级为-2的下标是0。

pendingmax记录的是每个等级已经记录的监视器个数。

pendingcnt记录的是每个等级中当前有效的监视器个数。这个值和ev_watcher中pending值有很大的相关性,之后我们会去将讨论。

pendings的每个元素是一个ANPENDING指针,其定义如下

typedef ev_watcher *W;/* stores the pending event set for a given watcher */
typedef struct
{W w;int events; /* the pending event set for the given watcher */
} ANPENDING;

成员变量w是一个ev_watcher指针,它指向anfds中一个监视器。我们看到这个结构中没有指向自身的指针,如next、pre之类,那就说明ANPENDING是用数组结构保存的,而非动态链表。

那么anfds中的数据是如何转移到pendings上的呢?这个工作是由ev_feed_event函数完成

void noinline
ev_feed_event (EV_P_ void *w, int revents) EV_THROW
{W w_ = (W)w;int pri = ABSPRI (w_);

w是监视器变量指针,revents是发生了的事件。ABSPRI宏将监视器中的等级转换成pendings数组下标,从而确定该监视器属于哪个数组。

在一次循环前,每个监视器的pending位都将是0。因为对于没有触发的事件,其默认是0;而对于本次触发的事件,则在事件对应的回调函数被执行前,pending值被设置为0。该pending位的作用是用于记录该监视器信息在相应等级pendings数组的子数组中的位置。

假如这个事件在一次循环中被触发两次。则第一次它会走入else的逻辑,根据pendingcnt中相应等级找到其应该属于的pending位数。如果此时pandings空间不足,则需要使用array_needsize重新分配并填充该空间;第二次时,pending位已经确定,此时只要更新events字段即可。

  if (expect_false (w_->pending))pendings [pri][w_->pending - 1].events |= revents;else{w_->pending = ++pendingcnt [pri];array_needsize (ANPENDING, pendings [pri], pendingmax [pri], w_->pending, EMPTY2);pendings [pri][w_->pending - 1].w      = w_;pendings [pri][w_->pending - 1].events = revents;}pendingpri = NUMPRI - 1;
}

pendings里保存的是事件已经被触发的监视器信息,这就包括回调已经被调用的和即将被调用的。对于回调已经被调用过的监视器,libev不会将其从数组中去掉,而只是简单的将其pending值设置为0。那么本次循环要遍历的ANPENDING元素个数可能比数组个数要少,其个数是pendingcnt数组中相应等级作为下标对应的值。

pendings中将数据准备好后,libev使用EV_INVOKE_PENDING宏遍历本次循环中更新的ANPENDING对象,调用其回调函数。

# define EV_INVOKE_PENDING ev_invoke_pending (EV_A)void noinline
ev_invoke_pending (EV_P)
{pendingpri = NUMPRI;while (pendingpri) /* pendingpri possibly gets modified in the inner loop */{--pendingpri;while (pendingcnt [pendingpri]){ANPENDING *p = pendings [pendingpri] + --pendingcnt [pendingpri];p->w->pending = 0;EV_CB_INVOKE (p->w, p->events);EV_FREQUENT_CHECK;}}
}#ifndef EV_CB_INVOKE
# define EV_CB_INVOKE(watcher,revents) (watcher)->cb (EV_A_ (watcher), (revents))
#endif

最后我们看下包括函数调用的结构图

libev源码解析——调度策略相关推荐

  1. libev源码解析——定时器监视器和组织形式

    我们先看下定时器监视器的数据结构.(转载请指明出于breaksoftware的csdn博客) /* invoked after a specific time, repeatable (based o ...

  2. libev源码解析——I/O模型

    在<libev源码解析--总览>一文中,我们介绍过,libev是一个基于事件的循环库.本文将介绍其和事件及循环之间的关系.(转载请指明出于breaksoftware的csdn博客) 目前i ...

  3. libev源码解析——定时器原理

    本文将回答<libev源码解析--I/O模型>中抛出的两个问题.(转载请指明出于breaksoftware的csdn博客) 对于问题1:为什么backend_poll函数需要指定超时?我们 ...

  4. libev源码解析——监视器(watcher)结构和组织形式

    在<libev源码解析--总览>中,我们介绍了libev的一些重要变量在不同编译参数下的定义位置.由于这些变量在多线程下没有同步问题,所以我们将问题简化,所提到的变量都是线程内部独有的,不 ...

  5. libev 源码解析

    一  libev简介 libev是一个轻量级的事件通知库,具备支持多种事件通知能力,通过对libev的源码的阅读,可以清楚了解事件通知实现内部机制. 二 核心数据结构 在libev中关键的数据结构是, ...

  6. libev源码解析——总览

    libev是个非常优秀的基于事件的循环库,很多开源软件,比如nodejs就是使用其实现基础功能.本系列将对该库进行源码分析.(转载请指明出于breaksoftware的csdn博客) 不知道是被墙了还 ...

  7. dubbo源码解析(九)远程通信——Transport层

    远程通讯--Transport层 目标:介绍Transport层的相关设计和逻辑.介绍dubbo-remoting-api中的transport包内的源码解析. 前言 先预警一下,该文篇幅会很长,做好 ...

  8. [源码解析] 模型并行分布式训练Megatron (5) --Pipedream Flush

    [源码解析] 模型并行分布式训练Megatron (5) --Pipedream Flush 文章目录 [源码解析] 模型并行分布式训练Megatron (5) --Pipedream Flush 0 ...

  9. 深度学习大模型训练--分布式 deepspeed PipeLine Parallelism 源码解析

    deepspeed PipeLine Parallelism 源码解析 basic concept PipeDream abstract 1F1B 4 steps Code comprehension ...

最新文章

  1. 分享五款java学习辅助工具,总有你用的上的~
  2. python遍历queryset_查询集 QuerySet
  3. 基于开源CA系统ejbca community 6.3.1.1构建私有CA管理数字证书
  4. go包package的使用细节
  5. 碎片化学前端,融入到积极上进的环境,我推荐~
  6. 表达式中常用到的运算符
  7. Docker知识体系--从容器基础-微服务-DevOps-实战演习-Kubernetes简介-KBS基础架构-KBS核心组件-KBS集群
  8. Java基础---数据类型转换和运算符
  9. 只腐蚀毛刺 腐蚀算法_去毛刺更省时省力的方式方法大全!
  10. bzoj4419 [Shoi2013]发微博 差分
  11. 使用Mac App Store更新、下载软件时出现未知错误的解决方法
  12. 软件著作权 php代码行数,申报软件著作权时,如何快捷计算源码行数
  13. 计算机相关扩展活动战队名字,有诗意的战队名字大全
  14. 测试用例(等价类法、边界值法、因果图法、判定表法、场景法、正交试验法、功能图法)
  15. 怀念父亲母亲-端午节快乐
  16. 数学 导数表 求导公式求导法则
  17. 护士副高需要计算机考试吗,护士评副高什么要求
  18. 医疗管理系统软件 linux系统,MyPatients 4.0.2 发布,医疗信息管理系统
  19. Java 并发编程实战演练
  20. 谷歌浏览器模拟微信/QQ内置浏览器调试及js判断方法

热门文章

  1. GitHub有趣分享:Thanos(灭霸命令)
  2. AI玩俄罗斯方块(Python实现)
  3. 二、如何读入图片、显示图像?
  4. Kimera实时重建的语义SLAM系统
  5. 在CentOS 6.6 64bit上安装截图软件shutter
  6. Python训练营2021:构建8个真实世界的Python项目
  7. UE5使用MetaHuman构建超现实的角色
  8. 单机 “5千万以上“ 工业级 LRU cache 实现
  9. C++ STL: 超详细 容器 deque 以及 适配器queue 和 stack 源码分析
  10. Linux性能分析命令工具汇总