libev源码解析——总览
libev是个非常优秀的基于事件的循环库,很多开源软件,比如nodejs就是使用其实现基础功能。本系列将对该库进行源码分析。(转载请指明出于breaksoftware的csdn博客)
不知道是被墙了还是网站不再维护,它的官网(http://libev.schmorp.de/)在国内已经没法访问了。但是我们仍然可以从github上下载其源码(https://github.com/enki/libev)。
使用样例
libev支持相对时间定时器、绝对时间定时器、文件状态监控和信号监控等功能。我们可以在它基础上,通过少量的代码实现稳健完善的功能。
我们先看一段实现定时器功能的代码
#include <ev.h>
#include <stdio.h>ev_timer timeout_watcher;static void
timeout_cb(EV_P_ ev_timer *w, int revents)
{puts("timeout");ev_break(EV_A_ EVBREAK_ONE);
}int main(void)
{struct ev_loop *loop = EV_DEFAULT;ev_timer_init(&timeout_watcher, timeout_cb, 5.5, 0);ev_timer_start(loop, &timeout_watcher);ev_run(loop, 0);return 0;
}
这段代码的结构非常简单。首先我们要定义一个名为timeout_cb的回调函数用于响应定时器。然后定义一个ev_timer结构(监视器),它通过ev_timer_init进行初始化。初始化的参数包含之前定义的响应函数指针和迭代器超时时间。ev_timer准备好后,通过ev_timer_start将其和一个ev_loop进行绑定。最后使用ev_run方法运行起来这个ev_loop指针,从而实现一个完整的定时器功能。
可见使用libev库非常方便。其实我们之后见到的其他用法和上面步骤是类似的,即:
- 初始化ev_loop。
- 定义监视器。
- 定义回调函数。
- 监视器和回调函数关联。
- 监视器和ev_loop关联。
- ev_run将ev_loop运行起来。
假如上面代码是个框架使用的雏形,那么如果让我们去设计这样的框架,该如何设计呢?
模型设计
首先我们需要考虑到的是使用sleep还是使用事件模型去实现逻辑等待。
如果使用sleep方法,那么我们就要在唤醒后去检测各个事件,比如要检测文件状态是否发生变化,比如定时器时间是否已经超时。于是有个问题,就是sleep多久怎么确定?我们不知道是5秒后还是1秒后文件状态发生变化,那么只能最小粒度sleep了。那么这就意味着线程在短暂挂起后,马上检测一系列可能尚未发生改变的事件。这种设计明显很消耗CPU,而且非常低效。
如果使用事件模型去等待,就可以解决上述问题。但是像定时器,在系统中并没有事件与其对应。于是我们需要考虑下对于没有事件对应的功能怎么通过事件模型去封装。
其次我们需要考虑使用单线程模型还是多线程模型。
单线程模型是让主流程和事件响应函数在一个线程中执行。其伪代码是
If (event is ready) {event_callback(); // in the main thead
}
其特点是实现简单,但是事件响应函数的效率将严重影响主流程对事件的响应速度。比如A、B两个事件同时发生,理论上我们希望两个事件的响应函数被同时执行,或者在允许存在的系统调用时间差(比如创建线程的消耗)内执行。然而单线程模型则会让一个响应函数执行完后再去执行另一响应函数,于是就出现排队现象。所以单线程模型无法保证及时响应。
多线程模型则完全避免了上述问题。它可在事件发生后启动一个线程去处理响应函数。当然相对来说多线程模型比较复杂,需要考虑多线程同步问题。
If (event is ready) {thread_excute(event_callback); // run in another thread
}
那么libev对上面两个问题是怎么选择的呢?对于sleep和事件模型,libev选择的是后者,所以它是“高性能”的。对于单线程和多线程,libev选择的是前者。至于原因我并不知道,可能是作者希望它足够简单,或者希望它能在不支持多线程的系统上使用。但是要说明一点,并不是说libev不支持多线程。因为一个单线程模型的执行体,是可以放在其他若干个线程中执行的,只要保证数据同步。
单/多线程编译
libev提供了各种编译选项以支持各种特性。比如在支持多线程的系统上,我们可以指定EV_MULTIPLICITY参数,以让libev编译出多线程版本。
libev对于单线程版本的数据都是以全局静态变量形式提供。而对于多线程版本,则提供了一个结构体——ev_loop保存数据,这样不同线程持有各自的数据对象,从而做到数据隔离。
#if EV_MULTIPLICITYstruct ev_loop{ev_tstamp ev_rt_now;#define ev_rt_now ((loop)->ev_rt_now)#define VAR(name,decl) decl;#include "ev_vars.h"#undef VAR};#include "ev_wrap.h"static struct ev_loop default_loop_struct;EV_API_DECL struct ev_loop *ev_default_loop_ptr = 0; /* needs to be initialised to make it a definition despite extern */#elseEV_API_DECL ev_tstamp ev_rt_now = 0; /* needs to be initialised to make it a definition despite extern */#define VAR(name,decl) static decl;#include "ev_vars.h"#undef VARstatic int ev_default_loop_ptr;#endif
不管是哪个版本,它们都提供了ev_default_loop_ptr变量。多线程版本它将指向全局静态变量default_loop_struct,这样对于使用了多线程版本又不想维护ev_loop结构对象的用户来说,直接使用这个对象就行了,非常方便。
然后再看下ev_vars.h的引入。其实现如下:
#define VARx(type,name) VAR(name, type name)VARx(ev_tstamp, now_floor) /* last time we refreshed rt_time */
VARx(ev_tstamp, mn_now) /* monotonic clock "now" */
VARx(ev_tstamp, rtmn_diff) /* difference realtime - monotonic time *//* for reverse feeding of events */
VARx(W *, rfeeds)
VARx(int, rfeedmax)
VARx(int, rfeedcnt)VAR (pendings, ANPENDING *pendings [NUMPRI])
VAR (pendingmax, int pendingmax [NUMPRI])
VAR (pendingcnt, int pendingcnt [NUMPRI])
……
在多线程版本中,它在ev_loop结构体中被引入的。这样在编译器展开文件时,它将会被定义到结构体内部。在单线程版本中,VAR宏被声明为定义一个静态全局变量的形式。这种利用宏和编译展开技术,在不同结构中定义相同类型数据的方式还是很有意思的。
但是又会有个问题,如何去访问这些变量呢?在单线程中,它们是静态变量,所有位置可以直接通过名称访问。而多线程版本中,则需要通过一个ev_loop结构体去引导。相关的代码总不能通过EV_MULTIPLICITY宏来区分不同变量形式吧?如果那样,代码将变得非常难看。我们看下libev怎么巧妙解决这个问题的。
上面代码块的多线程定义区间,引入了ev_wrap.h文件。其实现如下:
#ifndef EV_WRAP_H
#define EV_WRAP_H
#define acquire_cb ((loop)->acquire_cb)
#define activecnt ((loop)->activecnt)
#define anfdmax ((loop)->anfdmax)
#define anfds ((loop)->anfds)
#define async_pending ((loop)->async_pending)
#define asynccnt ((loop)->asynccnt)
……
这样使用一个和变量相同名称的宏替代了通过ev_loop结构体对象访问的变量。且这个宏名称和单线程版本中静态变量名相同。这样就让不同版本的关键变量“同名”了。于是代码对这些变量的访问直接使用其原始名称即可——单线程中使用的是真实变量名,多线程中使用的是宏。
这样的设计,又引入一个问题。那就是所有使用这些变量的函数,在多线程版本中,需要提供一个名字为loop的ev_loop结构体对象;而在单线程版本中则不需要。为了固化这个名称,libev还为此专门定义了一系列宏。
#if EV_MULTIPLICITY
struct ev_loop;
# define EV_P struct ev_loop *loop /* a loop as sole parameter in a declaration */
# define EV_P_ EV_P, /* a loop as first of multiple parameters */
# define EV_A loop /* a loop as sole argument to a function call */
# define EV_A_ EV_A, /* a loop as first of multiple arguments */
# define EV_DEFAULT_UC ev_default_loop_uc_ () /* the default loop, if initialised, as sole arg */
# define EV_DEFAULT_UC_ EV_DEFAULT_UC, /* the default loop as first of multiple arguments */
# define EV_DEFAULT ev_default_loop (0) /* the default loop as sole arg */
# define EV_DEFAULT_ EV_DEFAULT, /* the default loop as first of multiple arguments */
#else
# define EV_P void
# define EV_P_
# define EV_A
# define EV_A_
# define EV_DEFAULT
# define EV_DEFAULT_
# define EV_DEFAULT_UC
# define EV_DEFAULT_UC_
# undef EV_EMBED_ENABLE
#endif
之后我们在代码中导出可见的EV_P和EV_A就是为了保证不同版本的实现在代码层面是相同的。
相关文章:
GPT-2仅是“反刍”知识,真正理解语言还要改弦更张
作者 | Gary Marcus译者 | 泓技编辑 | 夕颜出品 | AI科技大本营(ID:rgznai100)【导读】OpenAI的GPT-2正被广泛地讨论,无论是《纽约客》还是《经济学人》,我们都能看到有关它的话题。关于自然和人工智能,它想…

sap business one 笑谈
Sap Business .e 出生在以色列,生下来的时候父母给起了个小名叫SBO,据说他的亲生父母是SAP家庭里的一个重要成员,后来SAP家族里的长老认为SBO长得不错,挺好看的。毕竟SAP家族里生下来的儿子都是胖胖的,想要个瘦点长相好…

来51学院的第一天
【来51学院的第一天】转载于:https://blog.51cto.com/10801189/1703279
libev源码解析——监视器(watcher)结构和组织形式
在《libev源码解析——总览》中,我们介绍了libev的一些重要变量在不同编译参数下的定义位置。由于这些变量在多线程下没有同步问题,所以我们将问题简化,所提到的变量都是线程内部独有的,不用考虑任何多线程问题。(转载…

《评人工智能如何走向新阶段》后记(再续16)
由AI科技大本营下载自视觉中国181.5种常见的机器学习方法。 (1)线性回归linear regression: 一种流行的回归算法,从样本特征的线性组合,linear combination中学习模型。 (2)负数几率回归,logis…

怎么样才能快速的把淘宝店铺推广出去
我来到淘宝近一个月了,目前顺利地得到了两颗心心.感触颇多.其中店铺的推广显得尤其重要,应很多淘友的提问,我把一些店铺推广技巧介绍如下,你如果觉得有益,就回一下贴,以示支持.在这里先谢谢了!先看第一板斧:一、修练内功ÿ…

linux的ftp服务器
2019独角兽企业重金招聘Python工程师标准>>> ftp服务器在网上较为常见,Linux ftp命令的功能是用命令的方式来控制在本地机和远程机之间传送文件,这里详细介绍Linux ftp命令的一些经常使用的命令,相信掌握了这些使用Linux 进行ftp操…

使用Forms Authentication实现用户注册、登录 (二)用户注册与登录
从这一部分开始,我们将通过一个实际的完整示例来看一下如何实现用户注册与登录。在介绍注册与登录之前,我们首先介绍一下如何判断用户是否已登录,并未后面的示例编写一些基础代码。 判断用户是否已经登录首先,在Web站点项目中添加…
libev源码解析——调度策略
在《libev源码解析——监视器(watcher)结构和组织形式》中介绍过,监视器分为[2,-2]区间5个等级的优先级。等级为2的监视器最高优,然后依次递减。不区分监视器类型和关联的文件描述符的值,权限高的要优先于权限低的执行…
特斯拉AI团队招兵买马:“英雄不问出处”
2月3日,特斯拉创始人兼CEO埃隆•马斯克发布推特,贴出了Autopilot AI团队招聘人才的信息。马斯克在推特中表示,特斯拉AI团队将直接向马斯克回报,他几乎每天都会与团队保持沟通和交流,并透露团队base在德州奥斯汀。据特斯…

java中重载与重写的区别
最近了解一下重载和重写 一、重载(Overloading) (1) 方法重载是让类以统一的方式处理不同类型数据的一种手段。多个同名函数同时存在,具有不同的参数个数/类型。 重载Overloading是一个类中多态性的一种表现。 (2) Ja…
libev源码解析——I/O模型
在《libev源码解析——总览》一文中,我们介绍过,libev是一个基于事件的循环库。本文将介绍其和事件及循环之间的关系。(转载请指明出于breaksoftware的csdn博客) 目前ibev支持如下IO事件模型: select模型。对应文件是…
“数学不好,干啥都不行!”骨灰级程序员:其实你们都是瞎努力
之前有很多程序员读者向我们抱怨:1)做算法优化时,只能现搬书里的算法,遇到不一样的问题,就不会了。2)面试一旦涉及到算法和数据结构,如果数学不行,面试基本就凉凉了。3)一…

VISTA中注册表项LEGACY_****的删除
在VISTA中如果你错误安装了某个驱动软件,而如果这个驱动安装软件考虑不周,无法卸载,那么你就麻烦了!比如我的U盘以前一直使用优易U盘加密软件1.2来做一个隐蔽的U盘。某天我在VISTA上运行了这个U盘加密软件,这个软件运行…
nodejs这个过程POST求
下面是一个web登陆模拟过程。当我们问一个链接,你得到一个表格,然后填写相应的表格值,然后提交登陆。 var http require(http); var querystring require(querystring); http.createServer(function (request, response) {var responseStri…

FTP、HTTP断点续传和多线程的协议基础
使用FTP或HTTP协议的下载软件支持断点续传和多线程的协议基础是:FTP用的是REST和SIZE;HTTP用的是Range。1、FTP实现断点续传的协议基础REST(有的服务器可能不支持此命令)Syntax: REST positionSets the point at which a file tra…
libev源码解析——定时器原理
本文将回答《libev源码解析——I/O模型》中抛出的两个问题。(转载请指明出于breaksoftware的csdn博客) 对于问题1:为什么backend_poll函数需要指定超时?我们让其一直等待到有事件发生不是更好么? 答案是“必须要指定超…
AI颠覆经济世界作用被夸大?影响远比媒体头条报道更加复杂
来源 | The Gradient译者 | 刘畅编辑 | 夕颜出品 | AI科技大本营(ID:rgznai100) 【导读】每天我们都听到有人声称人工智能将改变经济体系,造成大量的失业和垄断。但是,经济学家是如何看待的呢?在第三届AI经…

ArcGIS制图之Sub Points点抽稀
简介 Sub Points工具是 Esri 中国自主开发的一个插件,该工具优先考虑点在空间分布上的均匀合理性,并结合点数据中包含的 "优先级" 属性进行筛选。通过获取每个点在一定范围内拥有的相邻点的数目信息,得到地图中点密度的分布状况。抽…
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…
谁说AI无用?疫情下,AI已经代替人类做了很多...
整理 | 夕颜出品 | AI科技大本营(ID:rgznai100)经历过无比漫长的十多天,疫情至今还没有任何退散的迹象,形势越来越严峻。百度实时大数据报告显示,截至2020年2月4日9时,新型冠状病毒累计报告确诊病例20471例…

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

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

C# 视频监控系列(9):服务器端——数据捕获(抓图 + 录像)
前言 录像功能是监控系统中最重要的功能之一,除了本文的功能实现外,还需要你自己考虑合适的存储策略:存储大小、时间段、存储盘符等。 注意 本系列文章限于学习交流,注重过程,由于涉及公司,所以不提供源代码…
疫情当下,你是在家里躺着刷抖音?还是在做这些?
2020年本来可以是很开心的一年没想到一开头就给了我们一个重重的一击疫情的出现让我们非常的恐慌新型病毒肺炎让我们无处可躲原来热闹的新年因为疫情让我们逼不得已只能待在家里走亲访友更是不可能的就连原来约好的相亲也泡汤了因为封城、封村、封小区、封路了而这些也只是为了…
代码打补丁的利器——diff和patch
一般来说,如果我们在研发过程中需要对代码进行修改,是不需要通过打补丁的方式的,因为我们可以直接改动文件即可。但是如果针对一款要上线的产品,我们总不能在研发的电脑上编译通过后直接发布到线上的。(转载请指明出于…

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

NAT的配置与相关概念的理解
试验背景:随着接入因特网的计算机数量不断猛增,IPv4版本地址资源也就愈加显得捉襟见肘。好多企业申请的IP地址都是经过子网不断划分得到的。A类,B类地址基本已用完,而一般的用户根本就申请不到整段的公网C类地址。如果,…
AAAI 2020论文解读:商汤科技发布新视频语义分割和光流联合学习算法
来源 | Every Frame Counts: Joint Learning of Video Segmentation and Optical Flow编辑 | Carol出品 | AI科技大本营(ID:rgznai100) 商汤科技研究团队发表论文《Every Frame Counts: Joint Learning of VideoSegmentation and Optical Flo…