当前位置: 首页 > 编程日记 > 正文

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

我们先看下定时器监视器的数据结构。(转载请指明出于breaksoftware的csdn博客)

/* invoked after a specific time, repeatable (based on monotonic clock) */
/* revent EV_TIMEOUT */
typedef struct ev_timer
{EV_WATCHER_TIME (ev_timer)ev_tstamp repeat; /* rw */
} ev_timer;/* invoked at some specific time, possibly repeating at regular intervals (based on UTC) */
/* revent EV_PERIODIC */
typedef struct ev_periodic
{EV_WATCHER_TIME (ev_periodic)ev_tstamp offset; /* rw */ev_tstamp interval; /* rw */ev_tstamp (*reschedule_cb)(struct ev_periodic *w, ev_tstamp now) EV_THROW; /* rw */
} ev_periodic;

ev_timer是相对时间定时器监视器,ev_periodic是绝对时间定时器监视器。可以注意到,这两个监视器结构开头都拥有近乎相同的成员变量,即EV_WATCHER_TIME宏中定义的那部分

#define EV_WATCHER_TIME(type)			\EV_WATCHER (type)				\ev_tstamp at;     /* private */

为什么说“近乎相同”,是因为它们传递给EV_WATCHER宏的参数不同,而从会导致各自拥有一个名称不同的回调函数指针,而其他变量连名称都一样。

对这两种监视器,libev并没有像《libev源码解析——监视器(watcher)结构和组织形式》文中所述,将这些监视器关联到文件描述符作为下标的anfds结构中。

为什么不放在上述结构中?因为保存到anfds中的监视器,都是要求文件描述符对应的事件发生而被触发。而定时器是要求文件描述符对应的事件没有发生,通过等待超时而被触发(或者被其他无关事件触发,顺带执行)。所以将定时器监视器保存在这个结构中是没有用的。同时这也意味着,向pendings结构(《libev源码解析——调度策略》)中记录本次触发的监视器的数据来源并非只有anfds,还有定时器监视器相关的结构。

那这个结构是什么呢?我们先看下基础结构定义

  /* base class, nothing to see here unless you subclass */
typedef struct ev_watcher_time
{EV_WATCHER_TIME (ev_watcher_time)
} ev_watcher_time;typedef ev_watcher_time *WT;typedef struct {ev_tstamp at;WT w;
} ANHE;

ev_watcher_timer和ev_timer、ev_periodic有着近乎一致的数据格式——它们都是使用EV_WATCHER_TIME宏构成开始的几个成员变量。

这意味着,在忽略回调函数名的情况下,我们可以使用ev_watcher_timer指针指向ev_timer结构体对象或者ev_periodic结构体对象。

因为ANHE结构中变量w可以指向ev_timer,那么目前我们有两种保存定时器监视器的方式(以ev_timer为例):

  1. 连续的ev_timer结构体对象构成的数组
  2. 连续的ANHE结构对象构成的数组

这两种结构在内存中的布局如下:

这两种结构都有自己的优缺点:

  1. 结构比较简单,各个监视器的位置关系由数组下标维护。其缺点调整监视器位置比较低效,因为这意味着比较多的内存需要被转移。
  2. 结构则相对复杂,因为其通过一个额外ANHE数组维护各个监视器的关系。但是它可以方便的调整监视器的位置——只要调整各个指针即可。

对于结构的选择,应该依据应用场景决定。我们在《libev源码解析——定时器原理》中提到,libev需要寻找到“下次执行时间”离现在最近的监视器。如果采用连续的ev_timer结构存储,则可以分为如下两种处理方式:

  • ev_timer数组中各个元素位置不变,只是修改“下次执行时间”。对于这样乱序的数组,只有遍历的办法才能找到最近的。然而这种方式效率很低。
  • ev_timer数组中各个元素位置可变。这样在修改“下次执行时间”后,就需要对数组元素重新布局。

而如果采用连续的ANHE结构存储,则对于元素位置发生变化的场景,只要修改两个字段即可,而不用像上面方案那样要修改七个字段。相比较而言,这个方案更优。libev选用的就是这个方案。

void noinline
ev_timer_start (EV_P_ ev_timer *w) EV_THROW
{
……++timercnt;ev_start (EV_A_ (W)w, timercnt + HEAP0 - 1);array_needsize (ANHE, timers, timermax, ev_active (w) + 1, EMPTY2);ANHE_w (timers [ev_active (w)]) = (WT)w;ANHE_at_cache (timers [ev_active (w)]);
……
}

timers是保存“相对时间定时器”监视器的数组结构。在ev_periodic_start代码中,我们可以看到“绝对时间定时器”监视器保存在名字叫periodics的数组中。

下一步我们就需要寻找一种相对高效的排序算法。首先我们想到的是顺序排序,而且离现在时间最近的元素排在数组第一位。为了方便描述,我们之后将时间改成整数表示。假设我们有如下的监视器:1、3、5、7、9、11、13。如果时间为1的监视器执行了,则将其时间差改成12,则数组的移动方式如下


        w2~w6可以见得,“牵一发而动全身”。那是否有更好的方式?libev采用的是最小堆。关于最小堆操作的示例,可以参见《最小堆 构建、插入、删除的过程图解》。以上例,则操作如下图

可见,我们将操作元素的次数降低到3次,比顺序排列的方案要少很多。

这种操作是自上而下的,对应的代码是

inline_speed void
downheap (ANHE *heap, int N, int k)
{ANHE he = heap [k];for (;;){int c = k << 1;if (c >= N + HEAP0)break;c += c + 1 < N + HEAP0 && ANHE_at (heap [c]) > ANHE_at (heap [c + 1])? 1 : 0;if (ANHE_at (he) <= ANHE_at (heap [c]))break;heap [k] = heap [c];ev_active (ANHE_w (heap [k])) = k;k = c;}heap [k] = he;ev_active (ANHE_w (he)) = k;
}

这个行为发生在定时器监视器向pendings结构中记录时,因为离当前时间最近的监视器马上要执行,所以要修改它下一次执行的时间,然后重新布局结构。

inline_size void
timers_reify (EV_P)
{EV_FREQUENT_CHECK;if (timercnt && ANHE_at (timers [HEAP0]) < mn_now){do{ev_timer *w = (ev_timer *)ANHE_w (timers [HEAP0]);/*assert (("libev: inactive timer on timer heap detected", ev_is_active (w)));*//* first reschedule or stop timer */if (w->repeat){ev_at (w) += w->repeat;if (ev_at (w) < mn_now)ev_at (w) = mn_now;assert (("libev: negative ev_timer repeat value found while processing timers", w->repeat > 0.));ANHE_at_cache (timers [HEAP0]);downheap (timers, timercnt, HEAP0);

还有一种是自下而上的重新布局,这种发生在新增一个定时器时。


        相应代码是

inline_speed void
upheap (ANHE *heap, int k)
{ANHE he = heap [k];for (;;){int p = HPARENT (k);if (UPHEAP_DONE (p, k) || ANHE_at (heap [p]) <= ANHE_at (he))break;heap [k] = heap [p];ev_active (ANHE_w (heap [k])) = k;k = p;}heap [k] = he;ev_active (ANHE_w (he)) = k;
}
void noinline
ev_timer_start (EV_P_ ev_timer *w) EV_THROW
{if (expect_false (ev_is_active (w)))return;ev_at (w) += mn_now;assert (("libev: ev_timer_start called with negative timer repeat value", w->repeat >= 0.));EV_FREQUENT_CHECK;++timercnt;ev_start (EV_A_ (W)w, timercnt + HEAP0 - 1);array_needsize (ANHE, timers, timermax, ev_active (w) + 1, EMPTY2);ANHE_w (timers [ev_active (w)]) = (WT)w;ANHE_at_cache (timers [ev_active (w)]);upheap (timers, ev_active (w));EV_FREQUENT_CHECK;/*assert (("libev: internal timer heap corruption", timers [ev_active (w)] == (WT)w));*/
}

至此,《libev源码解析》系列已经讲完。经过这个系列分析,我们应该可以对libev整体实现原理和关键结构有了充分的了解。

至于具体实现,可以参见《libev中文手册》。

相关文章:

谁说AI无用?疫情下,AI已经代替人类做了很多...

整理 | 夕颜出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;经历过无比漫长的十多天&#xff0c;疫情至今还没有任何退散的迹象&#xff0c;形势越来越严峻。百度实时大数据报告显示&#xff0c;截至2020年2月4日9时&#xff0c;新型冠状病毒累计报告确诊病例20471例…

关于CSS样式浏览器兼容问题的一些注意事项

CSS技巧1.div的垂直居中问题 vertical-align:middle; 将行距增加到和整个DIV一样高 line-height:200px; 然后插入文字&#xff0c;就垂直居中了。缺点是要控制内容不要换行 2. margin加倍的问题 设置为float的div在ie下设置的margin会加倍。这是一个ie6都存在的bug。解决…

Ember.js 入门指南——查询记录

2019独角兽企业重金招聘Python工程师标准>>> store提供了统一的获取数据的接口。包括创建新记录、修改记录、删除记录等&#xff0c;更多有关Store API请看这个网址的介绍&#xff1a;http://devdocs.io/ember/data/classes/ds.store。 为了演示这些方法的使用我们结…

C# 视频监控系列(9):服务器端——数据捕获(抓图 + 录像)

前言 录像功能是监控系统中最重要的功能之一&#xff0c;除了本文的功能实现外&#xff0c;还需要你自己考虑合适的存储策略&#xff1a;存储大小、时间段、存储盘符等。 注意 本系列文章限于学习交流&#xff0c;注重过程&#xff0c;由于涉及公司&#xff0c;所以不提供源代码…

疫情当下,你是在家里躺着刷抖音?还是在做这些?

2020年本来可以是很开心的一年没想到一开头就给了我们一个重重的一击疫情的出现让我们非常的恐慌新型病毒肺炎让我们无处可躲原来热闹的新年因为疫情让我们逼不得已只能待在家里走亲访友更是不可能的就连原来约好的相亲也泡汤了因为封城、封村、封小区、封路了而这些也只是为了…

代码打补丁的利器——diff和patch

一般来说&#xff0c;如果我们在研发过程中需要对代码进行修改&#xff0c;是不需要通过打补丁的方式的&#xff0c;因为我们可以直接改动文件即可。但是如果针对一款要上线的产品&#xff0c;我们总不能在研发的电脑上编译通过后直接发布到线上的。&#xff08;转载请指明出于…

React Namespaced Components

2019独角兽企业重金招聘Python工程师标准>>> var MyForm React.createClass({...}); var MyForm.Row React.createClass({...}); var MyForm.Label React.createClass({...}); var MyForm.Input React.createClass({...}); This feature is available in v0.11 …

Linux下HOOK动态链接库中API的方法

2012年&#xff0c;我写了一篇介绍Windows系统下Ring3层API的hook方案——《一种注册表沙箱的思路、实现——Hook Nt函数》&#xff0c;其在底层使用了微软的Detours库。5年后&#xff0c;我又遇到这么一个问题&#xff0c;但是系统变成了Linux。我最开始的想法是找一个Linux下…

NAT的配置与相关概念的理解

试验背景&#xff1a;随着接入因特网的计算机数量不断猛增&#xff0c;IPv4版本地址资源也就愈加显得捉襟见肘。好多企业申请的IP地址都是经过子网不断划分得到的。A类&#xff0c;B类地址基本已用完&#xff0c;而一般的用户根本就申请不到整段的公网C类地址。如果&#xff0c…

AAAI 2020论文解读:商汤科技发布新视频语义分割和光流联合学习算法

来源 | Every Frame Counts: Joint Learning of Video Segmentation and Optical Flow编辑 | Carol出品 | AI科技大本营&#xff08;ID&#xff1a;rgznai100&#xff09; 商汤科技研究团队发表论文《Every Frame Counts: Joint Learning of VideoSegmentation and Optical Flo…

互联网+和创业潮,互联网+前提条件是什么?互联网+做什么?

在大众创业&#xff0c;万众创新的大浪下&#xff0c;凭着对新技术的敏感和青春激情&#xff0c;创业新军不断涌现.... 互联网创业浪潮, 如雨后春笋......,互联网渗透每个人的心中。创业不是赶时髦&#xff0c;而是一条非常孤独&#xff0c;艰难的路。实施“互联网&#xff0b;…

C++拾趣——C++11的语法糖auto

C是一种强类型的语言&#xff0c;比如变量a&#xff0c;如果声明它是整型&#xff0c;则之后只能将它作为整型来用。这和其他弱类型的语言有很大的区别&#xff0c;比如python中&#xff0c;我们可以让a在第一行是个整型&#xff0c;第三行是一个字符串。&#xff08;转载请指明…

“数学不行,啥都干不好!”骨灰级程序员:这比努力重要1000倍

之前有很多程序员读者向我们抱怨&#xff1a;1&#xff09;做算法优化时&#xff0c;只能现搬书里的算法&#xff0c;遇到不一样的问题&#xff0c;就不会了。2&#xff09;面试一旦涉及到算法和数据结构&#xff0c;如果数学不行&#xff0c;面试基本就凉凉了。3&#xff09;一…

跳槽 你准备好了吗

“人往高处走”&#xff0c;这固然没有错。但是&#xff0c;说来轻巧的一句话&#xff0c;它却包含了为什么“走”、什么是“高”、怎么“走”、什么时候“走”&#xff0c;以及“走”了以后怎么办等一系列问题。跳槽是一门学问&#xff0c;也是一种策略。“人往高处走”&#…

C++:常类型Const

常类型&#xff1a;使用类型修饰符const说明的类型&#xff0c;常类型的变量或对象成员的值在程序运行期间是不可改变的。 3.10.1 常引用 如果在说明引用时用const修饰&#xff0c;则被说明的引用为常引用。如果用常引用做形参&#xff0c;便不会产生对实参 的不希望的更改。常…

JQuery制作的toolTip,针对图片预览效果

昨天做了一个文字版的toolTip&#xff0c;后来想想现在大家都爱看图&#xff0c;文字未免有点单调了点&#xff0c;那我们就来个图片式的预览。代码比较简单&#xff0c;我就不多说了。 欢迎来到 买礼网 选购礼品&#xff01; 畅游鄂西山水风光尽在 恩施旅游资讯网首先看看调用…

29篇计算机视觉领域论文,篇篇惊艳!内附链接!

作者 | 微软亚洲研究院本文经授权转载自微软研究院AI头条&#xff08;ID&#xff1a;MSRAsia&#xff09;1. Deep High-Resolution Representation Learning for Human Pose Estimation论文链接&#xff1a;https://arxiv.org/pdf/1902.09212.pdf该论文在提出了一个新的网络Hig…

绑定CPU逻辑核心的利器——taskset

在工作中&#xff0c;我们可能遇到这样的需求&#xff1a;如何评估程序在一核和多核下的工作效率差距&#xff1f;最简单的想法是找一台只有一个CPU逻辑核的机器和一台有多个逻辑核的机器。&#xff08;转载请指明出于breaksoftware的csdn博客&#xff09;但是这种方式有明显的…

IDE set arguments

2019独角兽企业重金招聘Python工程师标准>>> code::blocks -> Project -->set programs arguments qtcreater -> Projects --> Build&Run --> Run --> Arguments xcode -> super < -->build-->arguments 转载于:https://my.osch…

2020年AI如何走?Jeff Dean和其他四位“大神”已做预测!

作者 | Khari Johnson译者 | 王艳妮 责编 | 胡巍巍出品 | AI科技大本营&#xff08;ID&#xff1a;rgznai100&#xff09;人工智能已经不再是随时准备改变世界的状态&#xff0c;而是已经在改变世界。在迈入2020年这新的一年、以及新的20年代之际&#xff0c;笔者请到了AI方面最…

zookeeper快速入门——简介

在几十年前&#xff0c;一个独立的计算机上往往部署着一套完整的应用系统。当时因为网络稳定性及速度的限制&#xff0c;将相关联的服务部署在一台机器上&#xff0c;让它们使用系统机制通信——比如管道&#xff0c;文件等&#xff0c;往往是最稳定和最高效的。然而随着网络技…

为TextMate扩展全屏功能

今天看代码&#xff0c;感觉TextMate那个窗口太小了点&#xff0c;越看越不爽&#xff0c;就想把它弄成全屏的。于是搜索啊搜索啊搜索&#xff0c;终于让我找到一款很yd的小软件&#xff0c;叫megazoomer&#xff0c; 下载地址是&#xff1a;[url]http://ianhenderson.org/mega…

hdu1406

一道很水很水的题&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;#include<iostream> using namespace std; int main(){int num1,num2,i,k,j,sum,n;while(cin>>n){ while(n--){cin&g…

zookeeper快速入门——部署

zookeeper有两种运行模式&#xff1a;独立模式和仲裁模式。独立模式就是只运行一个Zookeeper Server&#xff0c;这自然没法解决服务崩溃导致系统不可用的问题。仲裁模式就是以集群的方式运行Zookeeper Server&#xff0c;这样在Leader不可用时&#xff0c;集群内部会发起选举&…

2020,人工智能和深度学习未来的五大趋势

来源 | forbes编译 | Shawn编辑 | Carol出品 | AI科技大本营&#xff08;ID&#xff1a;rgznai100&#xff09;虽然近年来人工智能经常成为热门议题&#xff0c;但它还远未实现真正的成就。人工智能技术发展的主要障碍在于投资成本&#xff0c;投资成本影响短期内的回报。而当时…

电脑常见故障 1

死机恐怕是所有电脑故障里面最常见的一种了&#xff0c;但是死机的原因是多种多样的。 如果从硬件入手&#xff0c;先是看看机箱里的温度是否很高&#xff0c;要检查CPU的风扇是否正常运转&#xff0c;并要注意电脑的散热问题&#xff1b;其次可检查内存&#xff0c;检查完内存…

linux常用命令-date-clock-hwclock-type-whois--help-man-info-cal

date&#xff1a;时间管理电子表&#xff1a;晶体震荡器 石英震荡器Linux&#xff1a;rtc 硬件时间NTP&#xff1a;网络时间协义硬件时间&#xff08;命令&#xff1a;clock&#xff09;系统时间&#xff08;命令&#xff1a;date&#xff09;type COMMAND 判断命令是内部命令…

内存、性能问题分析的利器——valgraind

valgrind是一个知名的分析软件集。我们可以使用它进行内存、多线程及性能等各种问题的分析。它采用非侵入方式&#xff0c;所谓非侵入方式是指&#xff1a;我们不用在代码中插入分析工具的库。这对于开发者来说是友好的。因为如果要将工具编译到文件中&#xff0c;或者要调用其…

这是我见过最卡通的 Python 算法了,通俗易懂

普通程序员&#xff0c;不学算法&#xff0c;也可以成为大神吗&#xff1f;对不起&#xff0c;这个&#xff0c;绝对不可以。可是算法好难啊~~看两页书就想睡觉……所以就不学了吗&#xff1f;就一直当普通程序员吗&#xff1f;如果有一本算法书&#xff0c;看着很轻松……又有…

WebService(Axis2)视频教程与QQ交流群发布

Axis2是目前比较流行的WebService引擎。WebService被应用在很多不同的场景。例如&#xff0c;可以使用WebService来发布服务端 Java类的方法&#xff0c;以便使用不同的客户端进行调用。这样可以有效地集成多种不同的技术来完成应用系统。WebService还经常被使用在SOA中&#x…