定时器涉及到的结构体为:
struct timer_event {
//记录每个节点回复消息的信息,存储在节点的后面
uint32_t handle; //记录定位服务的编号
int session; //记录用于接收消息响应时,定位到是响应哪一条消息,由发送消息的服务生成
};
struct timer_node {
//节点
struct timer_node *next; //指向下一个节点
uint32_t expire; //保存该节点的timer_event消息回复事件的触发时间片为:添加时的时间片加上延时触发的时间
};
struct link_list {
//链表
struct timer_node head; //头节点,head.next指向第一个节点
struct timer_node *tail; //尾节点
};
struct timer {
//定时器的信息存储结构
struct link_list near[TIME_NEAR]; //保存时间片低8位的链表,每次都是从该数组中取链表
struct link_list t[4][TIME_LEVEL]; //保存时间片高24位的链表,按照0,1,2,3从低位到高位都分别对应6位
struct spinlock lock; //锁
uint32_t time; //当前的时间片,单位为1/100秒
uint32_t starttime; //系统的开始实时时间,从UTC1970-1-1 0:0:0开始计时,精确到秒
uint64_t current; //从开始时刻到现在的时长,精确到1/100秒
uint64_t current_point; //系统启动时长,精确到1/100秒
};
定时器的原理为:如上图所示,32为位无符号整数time记录时间片分别对应数组near[256]和t[4][64],每次添加节点时(skynet_timer.c文件中的add_node函数):(简单的说:如果expire与time之差小于256则将节点添加到near数组对应元素的链表中,否则从高位往低位依次比较expire的第i个6位二进制的值n与time的第i个6位二进制的值m,哪个不相等则将节点添加到数组t[4-i][n]对应的元素链表中),而下面是从低位往高位进行比较的。
首先检查节点的expire与time的高24位是否相等,相等则将该节点添加到expire低8位值对应数组near的元素的链表中,不相等则进行下一步。
检查expire与time的高18位是否相等,相等则将该节点添加到expire低第9位到第14位对应的6位二进制值对应数组t[0]的元素的链表中,如果不相等则进行下一步。
检查expire与time的高12位是否相等,相等则将该节点添加到expire低第15位到第20位对应的6位二进制值对应数组t[1]的元素的链表中,如果不相等则进行下一步。
检查expire与time的高6位是否相等,相等则将该节点添加到expire低第21位到第26位对应的6位二进制值对应数组t[2]的元素的链表中,如果不相等则进行下一步。
将该节点添加到expire低第27位到第32位对应的6位二进制值对应数组t[3]的元素的链表中
定时器线程每隔2500微秒会调用skynet_timer.c文件中的skynet_updatetime函数刷新时间,函数skynet_updatetime的原理为:将本次执行函数skynet_updatetime到上一次执行函数skynet_updatetime的这段时间间隔划分为以1/100秒为单位的时间片,并对这些时间片中的每个时间片都依次进行取操作(skynet_timer.c文件中的timer_execute函数)、刷新时间片time、移动t[4][64]中的链表操作以及取操作。取操作(timer_execute函数)的原理是将time的低8位值对应的near[256]数组中的链表取出,依次对链表中的所有节点进行消息回复。刷新时间片time、移动t[4][64]中的链表操作(timer_shift函数)的原理是刷新时间片time:
检查time是否溢出,如果溢出则将t[3][0]这个链表取出并依次将该链表中的节点添加(即实现该链表的移动操作),如果time未溢出,则进行下一步。
检查time低8位是否溢出产生进位,没有则结束,有则检查time的低第9位到第14位是否产生溢出,没有则将time的低第9位到第14位对应的值对应数组t[0]中的链表取出,并依次将该链表中的节点添加(即实现该链表的移动操作),如果有溢出,则进行下一步。
检查time低14位是否溢出产生进位,没有则结束,有则检查time的低第15位到第20位是否产生溢出,没有则将time的低第15位到第20位对应的值对应数组t[1]中的链表取出,并依次将该链表中的节点添加(即实现该链表的移动操作),如果有溢出,则进行下一步。
检查time低20位是否溢出产生进位,没有则结束,有则检查time的低第21位到第26位是否产生溢出,没有则将time的低第21位到第26位对应的值对应数组t[2]中的链表取出,并依次将该链表中的节点添加(即实现该链表的移动操作),如果有溢出,则进行下一步。
检查time低26位是否溢出产生进位,没有则结束,有则检查time的低第27位到第32位是否产生溢出,没有则将time的低第27位到第32位对应的值对应数组t[3]中的链表取出,并依次将该链表中的节点添加(即实现该链表的移动操作)。
在skynet_start.c文件这的skynet_start函数调用了skynet_timer.c文件中的skynet_timer_init函数进行定时器初始化
//初始化系统计时
void skynet_timer_init(void) {
TI = timer_create_timer(); //新建一个计时信息结构体
uint32_t current = 0;
systime(&TI->starttime, ¤t); //获取系统初始化时的UTC时间
TI->current = current;
TI->current_point = gettime(); //获得系统启动时的CPU时间
}
//创建一个计时信息结构体