swoole版本号:1.7.7-stable Github地址:点此查看


1.Timer

1.1.swTimer_interval_node

声明:

// swoole.h 1045-1050h
typedef struct _swTimer_interval_node
{
struct _swTimerList_node *next, *prev;
struct timeval lasttime;
uint32_t interval;
} swTimer_interval_node;
成员 说明
next,prev 链表的后继、前驱指针
struct timeval lasttime 持续时间
uint32_t interval 间隔时间

说明:

swTimer_interval_node结构体是一个链表节点,存放一个固定间隔的定时器,当中lasttime为当前定时器从上一次运行到如今经过的时间。interval存放了定时器间隔。

该结构体用于swoole原本的timer相关操作。

1.2.swTimer_node

声明:

// swoole.h 1052-1058h
typedef struct _swTimer_node
{
struct _swTimer_node *next, *prev;
void *data;
uint32_t exec_msec;
uint32_t interval;
} swTimer_node;
成员 说明
next,prev 链表的后继、前驱指针
void *data 数据域。存放额外的变量
uint32_t exec_msec 定时器应当运行的时间
uint32_t interval 间隔时间(无用,应废弃)

说明:

swTimer_node结构体是一个链表节点,存放一个须要在指定时间运行的定时器,当中exec_msec为当前定时器须要运行的指定时间,interval存放了定时器间隔。

该结构体用于swoole的after函数操作。

1.3.swTimer

声明:

// swoole.h 1060-1081h
typedef struct _swTimer
{
swTimer_node *root;
/*--------------timerfd & signal timer--------------*/
swHashMap *list;
int num;
int interval;
int use_pipe;
int lasttime;
int fd;
swPipe pipe;
/*-----------------for EventTimer-------------------*/
struct timeval basetime;
/*--------------------------------------------------*/
int (*add)(struct _swTimer *timer, int _msec, int _interval, void *data);
int (*del)(struct _swTimer *timer, int _interval_ms);
int (*select)(struct _swTimer *timer);
void (*free)(struct _swTimer *timer);
/*-----------------event callback-------------------*/
void (*onTimer)(struct _swTimer *timer, int interval_msec);
void (*onTimeout)(struct _swTimer *timer, void *data);
} swTimer;
成员 说明
swTimer_node *root after的链表根节点
swHashMap *list timer的链表根节点
int num 当前定时器的数量
int interval 定时器的基础响应间隔
int use_pipe 是否使用管道通信
int lasttime 持续时间(已废弃)
int fd 管道的写fd
swPipe pipe 管道
struct timeval basetime EventTimer的基础时间

说明:

swTimer结构体定时器的实体对象,用于存储、管理和运行众多定时任务,包含timer和after两种不同类型的定时任务。

1.4.swTimer公共操作函数

1.4.1.swTimer_init

声明:

// swoole.h 1083
int swTimer_init(int interval_ms, int no_pipe);

功能:初始化一个swTimer对象

核心源代码:

    // timer.c 38-94h
swTimer *timer = &SwooleG.timer;
timer->interval = interval;
timer->lasttime = interval; #ifndef HAVE_TIMERFD
SwooleG.use_timerfd = 0;
#endif timer->list = swHashMap_new(SW_HASHMAP_INIT_BUCKET_N, free);
if (!timer->list)
{
return SW_ERR;
} if (SwooleG.use_timerfd)
{
if (swTimer_timerfd_set(timer, interval) < 0)
{
return SW_ERR;
}
timer->use_pipe = 0;
}
else
{
if (use_pipe)
{
if (swPipeNotify_auto(&timer->pipe, 0, 0) < 0)
{
return SW_ERR;
}
timer->fd = timer->pipe.getFd(&timer->pipe, 0);
timer->use_pipe = 1;
}
else
{
timer->fd = 1;
timer->use_pipe = 0;
} if (swTimer_signal_set(timer, interval) < 0)
{
return SW_ERR;
}
swSignal_add(SIGALRM, swTimer_signal_handler);
} if (timer->fd > 1)
{
SwooleG.main_reactor->setHandle(SwooleG.main_reactor, SW_FD_TIMER, swTimer_event_handler);
SwooleG.main_reactor->add(SwooleG.main_reactor, SwooleG.timer.fd, SW_FD_TIMER);
} timer->add = swTimer_add;
timer->del = swTimer_del;
timer->select = swTimer_select;
timer->free = swTimer_free;

源代码解释:

获取SwooleG中的timer对象,设置timer响应间隔和lasttime參数。

假设未定义HAVE_TIMERFD。则设置不使用timerfd。

随后,使用HashMap初始化timer链表list。

假设使用了timerfd,调用swTimer_timerfd_set函数设置timer的基础响应间隔,并设置不使用管道。

假设不使用timerfd而使用signalfd,则先判定是否须要管道,假设须要。则创建管道并获取管道的写fd。随后。调用swTimer_signal_set函数设置Linux系统提供的精确定时器。并通过swSignal_add加入对SIGALRM信号的处理回调函数swTimer_signal_handler

接着,将管道写fd增加main_reactor的监听中。并设置回调函数swTimer_event_handler

最后设置swTimer的四个回调操作函数。

1.4.2.swTimer_signal_handler

声明:

// swoole.h 1085
void swTimer_signal_handler(int sig);

功能:SIGALRM信号的回调处理函数

核心源代码:

    // timer.c 338-344h
SwooleG.signal_alarm = 1;
uint64_t flag = 1; if (SwooleG.timer.use_pipe)
{
SwooleG.timer.pipe.write(&SwooleG.timer.pipe, &flag, sizeof(flag));
}

源代码解释:

设置SwooleG的signal_alarm标记为true,假设设定使用了管道。则通过管道发送一个flag通知Timer。

1.4.3.swTimer_event_handler

声明:

// swoole.h 1086
int swTimer_event_handler(swReactor *reactor, swEvent *event);

功能:timer的事件处理回调函数

核心源代码:

    // timer.c 323-334h
uint64_t exp;
swTimer *timer = &SwooleG.timer; if (read(timer->fd, &exp, sizeof(uint64_t)) < 0)
{
return SW_ERR;
}
SwooleG.signal_alarm = 0;
return swTimer_select(timer);

源代码解释:

尝试从管道中读取数据,假设读取成功,则重置SwooleG的signal_alarm标记,并调用swTimer_select来处理定时任务;

1.4.4.其它函数

swTimer_node_insert,swTimer_node_print,swTimer_node_delete。swTimer_node_destory四个函数是链表操作函数,不再具体分析。

1.5.Timer私有操作函数

1.5.1.swTimer_signal_set

声明:

// timer.c 24h
static int swTimer_signal_set(swTimer *timer, int interval);

功能:调用系统settimer函数启动定时器

核心源代码:

    struct itimerval timer_set;
int sec = interval / 1000;
int msec = (((float) interval / 1000) - sec) * 1000; struct timeval now;
if (gettimeofday(&now, NULL) < 0)
{
swWarn("gettimeofday() failed. Error: %s[%d]", strerror(errno), errno);
return SW_ERR;
} memset(&timer_set, 0, sizeof(timer_set));
timer_set.it_interval.tv_sec = sec;
timer_set.it_interval.tv_usec = msec * 1000; timer_set.it_value.tv_sec = sec;
timer_set.it_value.tv_usec = timer_set.it_interval.tv_usec; if (timer_set.it_value.tv_usec > 1e6)
{
timer_set.it_value.tv_usec = timer_set.it_value.tv_usec - 1e6;
timer_set.it_value.tv_sec += 1;
} if (setitimer(ITIMER_REAL, &timer_set, NULL) < 0)
{
swWarn("setitimer() failed. Error: %s[%d]", strerror(errno), errno);
return SW_ERR;
}

源代码解释:

首先将interval拆分成秒和毫秒,并将两个值加入进timer_set。随后调用setitimer函数设置系统定时器。

1.5.2.swTimer_timerfd_set

声明:

// timer.c 25h
static int swTimer_timerfd_set(swTimer *timer, int interval);

功能:调用timerfd相关函数启动timerfd定时器

核心源代码:

    // timer.c 100h
if (timer->fd == 0)
{
timer->fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC);
if (timer->fd < 0)
{
swWarn("timerfd_create() failed. Error: %s[%d]", strerror(errno), errno);
return SW_ERR;
}
} timer_set.it_interval.tv_sec = sec;
timer_set.it_interval.tv_nsec = msec * 1000 * 1000; timer_set.it_value.tv_sec = now.tv_sec + sec;
timer_set.it_value.tv_nsec = (now.tv_usec * 1000) + timer_set.it_interval.tv_nsec; if (timer_set.it_value.tv_nsec > 1e9)
{
timer_set.it_value.tv_nsec = timer_set.it_value.tv_nsec - 1e9;
timer_set.it_value.tv_sec += 1;
} if (timerfd_settime(timer->fd, TFD_TIMER_ABSTIME, &timer_set, NULL) == -1)
{
swWarn("timerfd_settime() failed. Error: %s[%d]", strerror(errno), errno);
return SW_ERR;
}

源代码解释:

调用timerfd_create函数创建一个timerfd。并将返回的fd赋值给timer.fd;随后设置timer_set的值。并调用timerfd_settime函数设置定时器相关属性。

1.5.3.swTimer_del

声明:

// timer.c 26h
static int swTimer_del(swTimer *timer, int ms);

功能:从timer的列表中移除一个指定定时器

核心源代码:

    swHashMap_del_int(timer->list, ms);
return SW_OK;

源代码解释:

从timer的list中移除ms相应的定时器

1.5.4.swTimer_free

声明:

// timer.c 27h
static void swTimer_free(swTimer *timer);

功能:释放timer的内存

核心源代码:

    swHashMap_free(timer->list);
if (timer->use_pipe)
{
timer->pipe.close(&timer->pipe);
}
else if (close(timer->fd) < 0)
{
swSysError("close(%d) failed.", timer->fd);
}
if (timer->root)
{
swTimer_node_destory(&timer->root);
}

源代码解释:

释放list,关闭管道,释放root指向的链表

1.5.5.swTimer_add

声明:

// timer.c 28h
static int swTimer_add(swTimer *timer, int msec, int interval, void *data);

功能:向timer中加入一个定时器

核心源代码:

    if (interval == 0)
{
return swTimer_addtimeout(timer, msec, data);
}
swTimer_interval_node *node = sw_malloc(sizeof(swTimer_interval_node));
if (node == NULL)
{
swWarn("malloc failed.");
return SW_ERR;
} bzero(node, sizeof(swTimer_interval_node));
node->interval = msec;
if (gettimeofday(&node->lasttime, NULL) < 0)
{
swSysError("gettimeofday() failed.");
return SW_ERR;
} if (msec < timer->interval)
{
int new_interval = swoole_common_divisor(msec, timer->interval);
timer->interval = new_interval;
swTimer_set(timer, new_interval);
}
swHashMap_add_int(timer->list, msec, node, NULL);
timer->num++;

源代码解释:

假设interval为0,说明这个定时器是个timeout类型定时器,调用swTimer_addtimeout函数。

否则,创建一个swTimer_interval_node结构体。设置其相关属性,并依据interval计算timer的基础响应间隔。并调用swTimer_set函数设置新的定时间隔。

最后,将新的定时任务节点加入进timer的list。并将定时器数量添加1。

1.5.6.swTimer_set

声明:

// timer.c 29h
static int swTimer_set(swTimer *timer, int new_interval);

功能:设置timer的定时器响应间隔

核心源代码:

    if (SwooleG.use_timerfd)
{
return swTimer_timerfd_set(timer, new_interval);
}
else
{
return swTimer_signal_set(timer, new_interval);
}

源代码解释:

假设使用了timerfd,调用swTimer_timerfd_set;否则,调用swTimer_signal_set;

1.5.7.swTimer_addtimeout

声明:

// timer.c 30h
int swTimer_addtimeout(swTimer *timer, int timeout_ms, void *data);

功能:从timer的列表中移除一个指定定时器

核心源代码:

    int new_interval = swoole_common_divisor(timeout_ms, timer->interval);
if (new_interval < timer->interval)
{
swTimer_set(timer, new_interval);
timer->interval = new_interval;
} struct timeval now;
if (gettimeofday(&now, NULL) < 0)
{
swWarn("gettimeofday() failed. Error: %s[%d]", strerror(errno), errno);
return SW_ERR;
} uint32_t now_ms = now.tv_sec * 1000 + now.tv_usec / 1000;
swTimer_node *node = sw_malloc(sizeof(swTimer_node));
if (node == NULL)
{
swWarn("malloc(%d) failed. Error: %s[%d]", (int ) sizeof(swTimer_node), strerror(errno), errno);
return SW_ERR;
} bzero(node, sizeof(swTimer_node));
node->data = data;
node->exec_msec = now_ms + timeout_ms;
swTimer_node_insert(&timer->root, node);

源代码解释:

首先计算timer定时器最小时间间隔,并设置新的定时器基础响应间隔。

随后创建新的swTimer_node节点,并设置其属性值,然后调用swTimer_node_insert函数在timer的root链表中加入新节点。(须要注意的是,由于这个定时器是一次性的。因此并不会改变timer->num的值)

1.5.8.swTimer_select

声明:

// timer.c 31h
int swTimer_select(swTimer *timer);

功能:遍历timer列表找到须要响应的定时器

核心源代码:

    uint64_t key;
swTimer_interval_node *timer_node;
struct timeval now; if (gettimeofday(&now, NULL) < 0)
{
swSysError("gettimeofday() failed.");
return SW_ERR;
}
//swWarn("%d.%d", now.tv_sec, now.tv_usec); if (timer->onTimeout == NULL)
{
swWarn("timer->onTimeout is NULL");
return SW_ERR;
}
/**
* timeout task list
*/
uint32_t now_ms = now.tv_sec * 1000 + now.tv_usec / 1000;
swTimer_node *tmp = timer->root;
while (tmp)
{
if (tmp->exec_msec > now_ms)
{
break;
}
else
{
timer->onTimeout(timer, tmp->data);
timer->root = tmp->next;
sw_free(tmp);
tmp = timer->root;
}
} if (timer->onTimer == NULL)
{
swWarn("timer->onTimer is NULL");
return SW_ERR;
}
uint32_t interval = 0;
do
{
//swWarn("timer foreach start\n----------------------------------------------");
timer_node = swHashMap_each_int(timer->list, &key); //hashmap empty
if (timer_node == NULL)
{
break;
}
//the interval time(ms)
interval = (now.tv_sec - timer_node->lasttime.tv_sec) * 1000 + (now.tv_usec - timer_node->lasttime.tv_usec) / 1000; /**
* deviation 1ms
*/
if (interval >= timer_node->interval - 1)
{
memcpy(&timer_node->lasttime, &now, sizeof(now));
timer->onTimer(timer, timer_node->interval);
}
} while (timer_node);

源代码解释:

首先获取当前系统时间。

判定onTimeout回调是否被设置,假设未设置则返回错误。随后,遍历timeout定时任务列表,找到exec_msec时间小于等于当前时间的任务,调用onTimeout响应这些回调,并移除该任务。

判定onTimer回调是否被设置,假设未设置则返回错误。

随后。遍历定时任务列表,判定当前节点是否须要响应(当前时间 - lasttime >= interval - 1ms),假设须要响应则设置新的lasttime并调用onTimer回调。

2.EventTimer

2.1.EventTimer原理

EventTimer的实现原理是利用了epoll的timeout超时设置。

通过设置epoll的timeout。就能在timeout时间后捕获一个事件。在捕获该事件后,通过遍历相应的事件列表就可以得知哪些事件须要处理。

2.2.EventTimer私有操作函数

2.2.1.swEventTimer_add

声明:

// EventTimer.c 19h
static int swEventTimer_add(swTimer *timer, int _msec, int interval, void *data);

功能:向timer中加入一个定时器

核心源代码:

    swTimer_node *node = sw_malloc(sizeof(swTimer_node));
if (!node)
{
swSysError("malloc(%d) failed.", (int )sizeof(swTimer_node));
return SW_ERR;
} int now_msec = swEventTimer_get_relative_msec();
if (now_msec < 0)
{
return SW_ERR;
}
node->data = data;
node->exec_msec = now_msec + _msec;
node->interval = interval ? _msec : 0;
swTimer_node_insert(&timer->root, node);

源代码解释:

初始化并向Timer的root中加入一个节点。

2.2.2.swEventTimer_del

声明:

// timer.c 20h
static int swEventTimer_del(swTimer *timer, int _msec);

功能:从timer的列表中移除一个指定定时器

核心源代码:

    if (timer->root)
{
swTimer_node_destory(&timer->root);
}

源代码解释:

从timer的root中移除ms相应的定时器

2.2.3.swEventTimer_select

声明:

// timer.c 21h
static int swEventTimer_select(swTimer *timer);

功能:从timer中选出须要响应的定时器

核心源代码:

    uint32_t now_msec = swEventTimer_get_relative_msec();
if (now_msec < 0)
{
return SW_ERR;
} swTimer_node *tmp = timer->root;
while (tmp)
{
if (tmp->exec_msec > now_msec)
{
break;
}
else
{
if (tmp->interval > 0)
{
timer->onTimer(timer, tmp->interval);
}
else
{
timer->onTimeout(timer, tmp->data);
} timer->root = tmp->next;
if (timer->root)
{
timer->root->prev = NULL;
}
if (tmp->interval > 0)
{
tmp->exec_msec += tmp->interval;
swTimer_node_insert(&SwooleG.timer.root, tmp);
}
else
{
sw_free(tmp);
}
tmp = timer->root;
}
}
if (timer->root == NULL)
{
SwooleG.main_reactor->timeout_msec = -1;
}
else
{
SwooleG.main_reactor->timeout_msec = timer->root->exec_msec - now_msec;
}

源代码解释:

遍历root链表。假设节点已经须要响应(exec_msec大于当前时间),则依据interval是否为0来运行各种不同的回调函数。而且假设interval为0,还须要移除当前节点。

最后,又一次设置SwooleG.main_reactor的timeout时间。假设timer中没有定时任务,则设定为无超时。

2.2.4.swEventTimer_free

声明:

// timer.c 22h
static void swEventTimer_free(swTimer *timer);

功能:释放timer

核心源代码:

    if (timer->root)
{
swTimer_node_destory(&timer->root);
}

源代码解释:

释放timer的root链表

Swoole源代码学习记录(十五)——Timer模块分析的更多相关文章

  1. Swoole源代码学习记录(十二)——ReactorThread模块

    Swoole版本号:1.7.5-stable Github地址:https://github.com/LinkedDestiny/swoole-src-analysis 这一章将分析Swoole的Re ...

  2. Swoole源代码学习记录(十三)——Server模块具体解释(上)

    Swoole版本号:1.7.5-stable Github地址:https://github.com/LinkedDestiny/swoole-src-analysis 最终能够正式进入Server. ...

  3. Python学习日记(十五) collections模块

    在内置函数(dict.list.set.tuple)的基础上,collections模块还提供了几个其他的数据类型:Counter.deque.defaultdict.namedtuple和Order ...

  4. 风炫安全WEB安全学习第二十五节课 利用XSS键盘记录

    风炫安全WEB安全学习第二十五节课 利用XSS键盘记录 XSS键盘记录 同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源.所以xyz.com下的js脚本采用a ...

  5. 学习笔记:CentOS7学习之十五: RAID磁盘阵列的原理与搭建

    目录 学习笔记:CentOS7学习之十五: RAID磁盘阵列的原理与搭建 14.1 RAID概念 14.1.1 RAID几种常见的类型 14.1.2 RAID-0工作原理 14.1.3 RAID-1工 ...

  6. python3.4学习笔记(十五) 字符串操作(string替换、删除、截取、复制、连接、比较、查找、包含、大小写转换、分割等)

    python3.4学习笔记(十五) 字符串操作(string替换.删除.截取.复制.连接.比较.查找.包含.大小写转换.分割等) python print 不换行(在后面加上,end=''),prin ...

  7. 201671010140. 2016-2017-2 《Java程序设计》java学习第十五周

    java学习第十五周 Java的GUI界面设计,框架以及主要部件填充,归置,布局管理,在第十一章和第十二章进行了系统的学习,在这两章的知识奠基下,可以简单的构造一个GUI用户界面,在两周的学习后,可以 ...

  8. python学习(十二)模块

    怎么一下子就来学了模块? 其实学了判断.循环.函数等知识就可以开始下水写程序了,不用在意其他的细节,等你用到的时候再回过头去看,此所谓囫囵吞枣学习法. 为啥学模块? 有点用的.或者有点规模的程序都是要 ...

  9. 我的Spring学习记录(五)

    在我的Spring学习记录(四)中使用了注解的方式对前面三篇做了总结.而这次,使用了用户登录及注册来对于本人前面四篇做一个应用案例,希望通过这个来对于我们的Spring的使用有一定的了解. 1. 程序 ...

随机推荐

  1. Linux 系统命令及其使用详解(大全)

    (来源: 中国系统分析员) cat cd chmod chown cp cut 1.名称:cat 使用权限:所有使用者 使用方式:cat [-AbeEnstTuv] [--help] [--versi ...

  2. js打开新的窗体不被浏览器阻止

    转载自js弹出新窗口而不会被浏览器阻止的方法有时候希望可以用js另开新窗口,但用window.open方法打开窗口总是被浏览器阻止, 可以用下面的方法打开新窗口而不会遭到拦截 1.新添加一个Form ...

  3. java高精度进制转换

    POJ1131   由于本题只有小数部分(整数部分均为0),故在进制转换的之后只能自己手写转换方法了.   8进制转换10进制的方法为,以0.75为例,应是7*8^-1 + 5*8^-2.所以呢,可以 ...

  4. c++:参数型别的推导

    STL源码剖析--侯捷 总结 尽管现在的很多语言支持参数类型的判别,但是c/c++并不支持这一特性. 但是我们可以通过一些技巧使得c++具有自动判别参数类型的特性. 模板 我们都知道在模板类和模板函数 ...

  5. 转:aptitude 命令详解

    原文:http://www.isspy.com/aptitude-%E5%91%BD%E4%BB%A4%E8%AF%A6%E8%A7%A3/ aptitude aptitude 是 Debian GN ...

  6. Hadoop完全分布式集群安装

    转载请注明原地址,谢谢! 本文目的是教大家配置Hadoop的完全分布式的集群,除了完全分布式还有两种分别是单节点和伪分布式部署.伪分布式只需要一台虚拟机,配置的东西也相对较少,大多用作代码调试,大家稍 ...

  7. One git command may cause you hacked(CVE-2014-9390)

    0x00 背景 CVE-2014-9390是最近很火的一个漏洞,一个git命令就可能导致你被黑,我不打算深入探讨这个漏洞的细节,官方已经在https://github.com/blog/1938-gi ...

  8. angular2 学习笔记 (Pipes)

    Pipe 就是 ng1 的 filter <pre>{{ jsonValue | json }}</pre> 用法看这里就很清楚了 : https://angular.cn/d ...

  9. Networking

    poj1287:http://poj.org/problem?id=1287 题意:平面上有许多点,又有许多边.每条边有一定的长度,且只会连接两个不同的点.现需要从这些边中选择某些边,使得尽可能多的点 ...

  10. 一句话改变TWinControl控件的left坐标的前世今生(入口函数是SetBounds,然后调用SetWindowPos起作用,并发消息更新Delphi的left属性值)

    Delphi的重要属性,主要是Enable,  Visible, Color, left等等.这里分析left,因为TWinControl里有些覆盖函数的原因,虽然起点都是TControl.SetLe ...