libev源码解析——监视器(watcher)结构和组织形式
在《libev源码解析——总览》中,我们介绍了libev的一些重要变量在不同编译参数下的定义位置。由于这些变量在多线程下没有同步问题,所以我们将问题简化,所提到的变量都是线程内部独有的,不用考虑任何多线程问题。(转载请指明出于breaksoftware的csdn博客)
之前提到过,libev支持多种功能,比如文件状态监控、定时器等。这些功能都是有其相对应的一个“监视器”(watcher),比如文件监视器、相对时间定时器监视器等。虽然这些监视器很多,但是它们都共有一些属性
#ifndef EV_COMMON
# define EV_COMMON void *data;
#endif#ifndef EV_CB_DECLARE
# define EV_CB_DECLARE(type) void (*cb)(EV_P_ struct type *w, int revents);
#endif#if EV_MINPRI == EV_MAXPRI
# define EV_DECL_PRIORITY
#elif !defined (EV_DECL_PRIORITY)
# define EV_DECL_PRIORITY int priority;
#endif/* shared by all watchers */
#define EV_WATCHER(type) \int active; /* private */ \int pending; /* private */ \EV_DECL_PRIORITY /* private */ \EV_COMMON /* rw */ \EV_CB_DECLARE (type) /* private */
active表示这个监视器是否处于激活状态。
priority表示监视器的优先级,其值可以从-2~2,共5个级别。其中2是最高级别,-2是最低级别。级别高的监视器会优先于级别低的监视器执行。
cb是事件响应函数指针,data则是用于保存用户自定义的数据。这样的组合设计在使用回调函数的开源库中很常见。因为回调的调用机会并不由我们掌握,我们无法区分每次回调对应于我们哪次注册行为。而可以通过在向框架注册回调函数时保存回调调用的数据来达到区分的目的。
pending用于表示该监视器在触发过的相同优先级下所有监视器数组的索引下标。因为相同优先级的监视器可能有很多,所以我们需要一个结构保存这样的一组数据,于是就需要索引/下标进行区分。这块信息我们将在《libev使用方法和源码解析——关键结构和基本原理2》介绍。
最简单的监视器,也是最基础的监视器是ev_watcher。它只具有EV_WATCHER声明的变量
typedef struct ev_watcher
{EV_WATCHER (ev_watcher)
} ev_watcher;
由于相同类型的监视器可能有多个,所以我们需要一个结构保存这么一组监视器。于是libev使用链表的形式保存这样的数据。那使用什么类型的链表呢?如果我们每个监视器的内存结构大小相同,则我们可以使用连续的内存结构。可是之后我们会介绍到,不同监视器的大小是不一样的。于是libev使用的是堆上分配的单向链表结构。至于实现,我们只要在结构中适当位置保存指向下一个结构地址的指针即可
#define EV_WATCHER_LIST(type) \EV_WATCHER (type) \struct ev_watcher_list *next; /* private */typedef struct ev_watcher_list
{EV_WATCHER_LIST (ev_watcher_list)
} ev_watcher_list;
其他监视器都是在此结构末尾追加了各自需要记录的数据。比如IO监视器和子进程监视器
typedef struct ev_io
{EV_WATCHER_LIST (ev_io)int fd; /* ro */int events; /* ro */
} ev_io;typedef struct ev_child
{EV_WATCHER_LIST (ev_child)int flags; /* private */int pid; /* ro */int rpid; /* rw, holds the received pid */int rstatus; /* rw, holds the exit status, use the macros from sys/wait.h */
} ev_child;
我们需要注意下这样设计的用意。从下图可见,任何监视器都可以被按ev_watcher大小准确切分,这意味着我们可以使用ev_watcher指向任何监视器结构体。同样的,还可以使用ev_watcher_list指向任何监视器。这种设计的优点将在后面展现出来。
看了这些监视器,我们还不能察觉到libev的底层原理。现在我们回忆下之前的介绍——libev是一个基于事件的循环库。那么事件将是一个核心,然而事件需要一个文件描述符(fd)。文件描述符将和这些监视器如何协作呢?
我们可以想象出,一个文件描述符应该关联起来多个监视器。比如我们要监视一个文件是否可读,那么这个监视器将和文件描述符关联。我们还要监视这个文件是否可写入,那么又有一个监视器和这个文件描述符关联。那么这些不同的监视器将如何围绕在这个文件描述符周围呢?链表!之前提到的ev_watcher_list链表,它可以把不同类型的监视器连接在一起。libev就是这么做的,它定义了一个结构ANFD
typedef ev_watcher_list *WL;typedef struct
{WL head;unsigned char events; /* the events watched for */unsigned char reify; /* flag set when this ANFD needs reification (EV_ANFD_REIFY, EV__IOFDSET) */unsigned char emask; /* the epoll backend stores the actual kernel mask in here */unsigned char unused;
#if EV_USE_EPOLLunsigned int egen; /* generation counter to counter epoll bugs */
#endif
#if EV_SELECT_IS_WINSOCKET || EV_USE_IOCPSOCKET handle;
#endif
#if EV_USE_IOCPOVERLAPPED or, ow;
#endif
} ANFD;
我们只需要关注head和events变量。
head从名字上就可以看出它是一个监视器链表的头。这儿提一句,我们看到这是一个单向链表,这也意味着以后要对这个链表进行元素新增很有可能是在头部插入,因为那样做最高效了。
events变量表示和文件描述符关联的事件,为什么要记录这个数据呢?继续以之前的例子为例,我们先要监控这个文件的可读,于是events是EV_READ;现在我们还要监控其可写,于是events变成EV_WRITE|EV_READ,而相应的head下将有两个监视器。此时IO模型(select/poll/epoll等)将监视该文件描述符的可读可写,如果发生任何之一,将使用发生的事件去head下各个监视器去匹配。
ANFD结构不需要再扩展了,于是它的结构是稳定的。所以我们可以使用连续的地址空间去保存一组信息。而且我们可以使用文件描述符的值去做其数组下标,这样就可以很方便通过文件描述符找到其对应的监视器链表。
当然ANFD使用连续内存也是有个前提的,就是文件描述符的值必须在一定的值以下。为什么呢?比如文件描述符的值如果能达到0xFFFFFFFF,那么这个数组要有0xFFFFFFFF个元素?这明显是不能接受的。所幸,系统的文件描述符值的上限只有几万。
在libev中,它使用anfds保存上述数组。数组的大小也并非一开始就使用文件描述符上限值,而是随着使用的文件描述符值增大而增大。
#define array_needsize(type,base,cur,cnt,init) \if (expect_false ((cnt) > (cur))) \{ \int ecb_unused ocur_ = (cur); \(base) = (type *)array_realloc \(sizeof (type), (base), &(cur), (cnt)); \init ((base) + (ocur_), (cur) - ocur_); \}void noinline
ev_io_start (EV_P_ ev_io *w) EV_THROW
{int fd = w->fd;if (expect_false (ev_is_active (w)))return;assert (("libev: ev_io_start called with negative fd", fd >= 0));assert (("libev: ev_io_start called with illegal event mask", !(w->events & ~(EV__IOFDSET | EV_READ | EV_WRITE))));EV_FREQUENT_CHECK;ev_start (EV_A_ (W)w, 1);array_needsize (ANFD, anfds, anfdmax, fd + 1, array_init_zero);
数组重分配后,就让文件描述符值作为下标的ANFD结构的head指向新的监视器
wlist_add (&anfds[fd].head, (WL)w);
理论上来说,我们有了这么一个结构就可以满足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…

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

C++拾趣——C++11的语法糖auto
C是一种强类型的语言,比如变量a,如果声明它是整型,则之后只能将它作为整型来用。这和其他弱类型的语言有很大的区别,比如python中,我们可以让a在第一行是个整型,第三行是一个字符串。(转载请指明…
“数学不行,啥都干不好!”骨灰级程序员:这比努力重要1000倍
之前有很多程序员读者向我们抱怨:1)做算法优化时,只能现搬书里的算法,遇到不一样的问题,就不会了。2)面试一旦涉及到算法和数据结构,如果数学不行,面试基本就凉凉了。3)一…

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