时间轮 (Timing-Wheel) 算法类似于一以恒定速度旋转的左轮手枪,枪的撞针则撞击枪膛,如果枪膛中有子弹,则会被击发;与之相对应的是:对于 PerTickBookkeeping,其最本质的工作在于以 Tick 为单位增加时钟,如果发现有任何定时器到期,则调用相应的 ExpiryProcessing。设定一个循环为 N 个 Tick 单元,当前时间是在 S 个循环之后指向元素 i (i>=0 and i<= N - 1),则当前时间 (Current Time)Tc 可以表示为:Tc = S*N + i ;如果此时插入一个时间间隔 (Time Interval) 为 Ti 的定时器,设定它将会放入元素 n(Next) 中,则 n = (Tc + Ti)mod N = (S*N + i + Ti) mod N = (i + Ti) mod N 。如果我们的 N 足够的大,显然 StartTimer,StopTimer,PerTickBookkeeping 时,算法复杂度分别为 O(1),O(1),O(1) 。下图是一个简单的时间轮定时器:
如果需要支持的定时器范围非常的大,上面的实现方式则不能满足这样的需求。因为这样将消耗非常可观的内存,假设需要表示的定时器范围为:0 – 2^3-1ticks,则简单时间轮需要 2^32 个元素空间,这对于内存空间的使用将非常的庞大。也许可以降低定时器的精度,使得每个 Tick 表示的时间更长一些,但这样的代价是定时器的精度将大打折扣。现在的问题是,度量定时器的粒度,只能使用唯一粒度吗?想想日常生活中常遇到的水表,如下图 :
在上面的水表中,为了表示度量范围,分成了不同的单位,比如 1000,100,10 等等,相似的,表示一个 32bits 的范围,也不需要 2^32 个元素的数组。实际上,Linux 的内核把定时器分为 5 组,每组的粒度(对应水表例子的单位)分别表示为:1 jiffies,256 jiffies,256*64 jiffies,256*64*64 jiffies,256*64*64*64 jiffies,每组中桶的数量分别为:256,64,64,64,64,能表示的范围为 2^32 。有了这样的实现,驱动内核定时器的机制也可以通过水表的例子来理解了,就像水表,每个粒度上都有一个指针指向当前时间,时间以固定 tick 递增,而当前时间指针则也依次递增,如果发现当前指针的位置可以确定为一个注册的定时器,就触发其注册的回调函数。 Linux 内核定时器本质上是 Single-Shot Timer,如果想成为 Repeating Timer,可以在注册的回调函数中再次的注册自己。以下是实现代码:
// def.h #ifndef _DEF_H_ #define _DEF_H_ #if defined(_WIN32) || defined(_WIN64) #define STDCALL __stdcall #else #define STDCALL __attribute__((stdcall)) #endif //基本数据类型定义 typedef char int8 ; typedef unsigned char uint8 ; typedef uint8 byte ; typedef short int16 ; typedef unsigned short uint16 ; typedef long int32 ; typedef unsigned long uint32 ; #endif //_DEF_H_
// Lock.h 同步锁 #ifndef _LOCK_H_ #define _LOCK_H_ #if defined(_WIN32) || defined(_WIN64) #include <Windows.h> typedef CRITICAL_SECTION LOCK ; #else #include <pthread.h> typedef pthread_mutex_t LOCK ; #endif void InitLock(LOCK *pLock) ; void UninitLock(LOCK *pLock) ; void Lock(LOCK *pLock) ; void Unlock(LOCK *pLock) ; #endif //_LOCK_H_
// Lock.c 同步锁 #include "Lock.h" void InitLock(LOCK *pLock) { #if defined(_WIN32) || defined(_WIN64) InitializeCriticalSection(pLock) ; #else pthread_mutex_init(pLock, NULL); #endif } void UninitLock(LOCK *pLock) { #if defined(_WIN32) || defined(_WIN64) DeleteCriticalSection(pLock) ; #else pthread_mutex_destroy(pLock); #endif } void Lock(LOCK *pLock) { #if defined(_WIN32) || defined(_WIN64) EnterCriticalSection(pLock) ; #else pthread_mutex_lock(pLock); #endif } void Unlock(LOCK *pLock) { #if defined(_WIN32) || defined(_WIN64) LeaveCriticalSection(pLock) ; #else pthread_mutex_unlock(pLock); #endif }
// Thread.h 定时器调度线程 #ifndef _THREAD_H_ #define _THREAD_H_ #if defined(_WIN32) || defined(_WIN64) #include <Windows.h> #include <process.h> //_beginthreadex() typedef HANDLE THREAD ; #else #include <pthread.h> typedef pthread_t THREAD ; #endif #include "def.h" typedef void* (*FNTHREAD)(void *pParam); THREAD ThreadCreate(FNTHREAD fnThreadProc, void *pParam) ; void ThreadJoin(THREAD thread) ; void ThreadDestroy(THREAD thread) ; #endif //_THREAD_H_
// Thread.c 定时器调度线程 #include "Thread.h" THREAD ThreadCreate(FNTHREAD fnThreadProc, void *pParam) { #if defined(_WIN32) || defined(_WIN64) if(fnThreadProc == NULL) return NULL ; return (THREAD)_beginthreadex(NULL, 0, (LPTHREAD_START_ROUTINE)fnThreadProc, pParam, 0, NULL) ; #else THREAD t ; if(fnThreadProc == NULL) return 0 ; if(pthread_create(&t, NULL, fnThreadProc, pParam) == 0) return t ; else return (THREAD)0 ; #endif } void ThreadJoin(THREAD thread) { #if defined(_WIN32) || defined(_WIN64) WaitForSingleObject(thread, INFINITE) ; #else pthread_join(thread, NULL) ; #endif } void ThreadDestroy(THREAD thread) { #if defined(_WIN32) || defined(_WIN64) CloseHandle(thread) ; #else // #endif }
// Timer.h #ifndef _TIMER_H_ #define _TIMER_H_ #include "def.h" #include "Lock.h" #include "Thread.h" #define CONFIG_BASE_SMALL 0 //TVN_SIZE=64 TVR_SIZE=256 #define TVN_BITS (CONFIG_BASE_SMALL ? 4 : 6) #define TVR_BITS (CONFIG_BASE_SMALL ? 6 : 8) #define TVN_SIZE (1 << TVN_BITS) #define TVR_SIZE (1 << TVR_BITS) #define TVN_MASK (TVN_SIZE - 1) #define TVR_MASK (TVR_SIZE - 1) #define MAX_TVAL ((unsigned long)((1ULL << (TVR_BITS + 4*TVN_BITS)) - 1)) #define TIME_AFTER(a,b) ((long)(b) - (long)(a) < 0) #define TIME_BEFORE(a,b) TIME_AFTER(b,a) #define TIME_AFTER_EQ(a,b) ((long)(a) - (long)(b) >= 0) #define TIME_BEFORE_EQ(a,b) TIME_AFTER_EQ(b,a) typedef void (STDCALL *FNTIMRCALLBACK)(void *pParam) ; typedef struct LIST_TIMER { struct LIST_TIMER *pPrev ; struct LIST_TIMER *pNext ; } LISTTIMER, *LPLISTTIMER ; typedef struct TIMER_NODE { struct LIST_TIMER ltTimer ; //定时器链表的入口 uint32 uExpires ; //定时器超时的时刻 uint32 uPeriod ; //间隔多长时间触发一次 FNTIMRCALLBACK fnTimer ; //定时器处理函数 void *pParam ; //回调函数的参数 } TIMERNODE, *LPTIMERNODE ; typedef struct TIMER_MANAGER { LOCK lock ; //同步锁 THREAD thread ; //线程句柄 uint32 uExitFlag ; //退出标识(0:Continue, other: Exit) uint32 uJiffies ; //基准时间(当前时间) struct LIST_TIMER arrListTimer1[TVR_SIZE] ; struct LIST_TIMER arrListTimer2[TVN_SIZE] ; struct LIST_TIMER arrListTimer3[TVN_SIZE] ; struct LIST_TIMER arrListTimer4[TVN_SIZE] ; struct LIST_TIMER arrListTimer5[TVN_SIZE] ; } TIMERMANAGER, *LPTIMERMANAGER ; void STDCALL SleepMilliseconds(uint32 uMs) ; //创建定时器管理器 LPTIMERMANAGER STDCALL CreateTimerManager(void) ; //删除定时器管理器 void STDCALL DestroyTimerManager(LPTIMERMANAGER lpTimerManager) ; //创建一个定时器。fnTimer回调函数地址。pParam回调函数的参数。uDueTime首次触发的超时时间间隔。uPeriod定时器循环周期,若为0,则该定时器只运行一次。 LPTIMERNODE STDCALL CreateTimer(LPTIMERMANAGER lpTimerManager, FNTIMRCALLBACK fnTimer, void *pParam, uint32 uDueTime, uint32 uPeriod) ; //删除定时器 int32 STDCALL DeleteTimer(LPTIMERMANAGER lpTimerManager, LPTIMERNODE lpTimer) ; #endif //_TIMER_H_
// Timer.c 定时器实现 #include <stddef.h> #include <stdlib.h> #if defined(_WIN32) || defined(_WIN64) #include <time.h> #else #include <sys/time.h> #endif #include "Timer.h" //获取基准时间。 static uint32 GetJiffies_old(void) { #if defined(_WIN32) || defined(_WIN64) SYSTEMTIME st ; struct tm t ; GetLocalTime(&st); t.tm_year = st.wYear - 1900; t.tm_mon = st.wMonth - 1; t.tm_mday = st.wDay; t.tm_hour = st.wHour; t.tm_min = st.wMinute; t.tm_sec = st.wSecond; return (uint32)mktime(&t) * 1000 + st.wMilliseconds ; #else struct timeval tv ; gettimeofday(&tv, NULL) ; return tv.tv_sec * 1000 + tv.tv_usec / 1000 ; #endif } static uint32 GetJiffies(void) { #if defined(_WIN32) || defined(_WIN64) return GetTickCount() ; #else //需连接 rt库,加-lrt参数 struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000); #endif } static void ListTimerInsert(struct LIST_TIMER *pNew, struct LIST_TIMER *pPrev, struct LIST_TIMER *pNext) { pNext->pPrev = pNew; pNew->pNext = pNext; pNew->pPrev = pPrev; pPrev->pNext = pNew; } static void ListTimerInsertHead(struct LIST_TIMER *pNew, struct LIST_TIMER *pHead) { ListTimerInsert(pNew, pHead, pHead->pNext); } static void ListTimerInsertTail(struct LIST_TIMER *pNew, struct LIST_TIMER *pHead) { ListTimerInsert(pNew, pHead->pPrev, pHead); } static void ListTimerReplace(struct LIST_TIMER *pOld, struct LIST_TIMER *pNew) { pNew->pNext = pOld->pNext ; pNew->pNext->pPrev = pNew ; pNew->pPrev = pOld->pPrev ; pNew->pPrev->pNext = pNew ; } static void ListTimerReplaceInit(struct LIST_TIMER *pOld, struct LIST_TIMER *pNew) { ListTimerReplace(pOld, pNew) ; pOld->pNext = pOld ; pOld->pPrev = pOld ; } static void InitArrayListTimer(struct LIST_TIMER *arrListTimer, uint32 nSize) { uint32 i ; for(i=0; i<nSize; i++) { arrListTimer[i].pPrev = &arrListTimer[i] ; arrListTimer[i].pNext = &arrListTimer[i] ; } } static void DeleteArrayListTimer(struct LIST_TIMER *arrListTimer, uint32 uSize) { struct LIST_TIMER listTmr, *pListTimer ; struct TIMER_NODE *pTmr ; uint32 idx ; for(idx=0; idx<uSize; idx++) { ListTimerReplaceInit(&arrListTimer[idx], &listTmr) ; pListTimer = listTmr.pNext ; while(pListTimer != &listTmr) { pTmr = (struct TIMER_NODE *)((uint8 *)pListTimer - offsetof(struct TIMER_NODE, ltTimer)) ; pListTimer = pListTimer->pNext ; free(pTmr) ; } } } static void AddTimer(LPTIMERMANAGER lpTimerManager, LPTIMERNODE pTmr) { struct LIST_TIMER *pHead ; uint32 i, uDueTime, uExpires ; // uExpires = pTmr->uExpires ; //定时器到期的时刻 uDueTime = uExpires - lpTimerManager->uJiffies ; if (uDueTime < TVR_SIZE) //idx < 256 (2的8次方) { i = uExpires & TVR_MASK; //expires & 255 pHead = &lpTimerManager->arrListTimer1[i] ; } else if (uDueTime < 1 << (TVR_BITS + TVN_BITS)) //idx < 16384 (2的14次方) { i = (uExpires >> TVR_BITS) & TVN_MASK; // i = (expires>>8) & 63 pHead = &lpTimerManager->arrListTimer2[i] ; } else if (uDueTime < 1 << (TVR_BITS + 2 * TVN_BITS)) // idx < 1048576 (2的20次方) { i = (uExpires >> (TVR_BITS + TVN_BITS)) & TVN_MASK; // i = (expires>>14) & 63 pHead = &lpTimerManager->arrListTimer3[i] ; } else if (uDueTime < 1 << (TVR_BITS + 3 * TVN_BITS)) // idx < 67108864 (2的26次方) { i = (uExpires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK; // i = (expires>>20) & 63 pHead = &lpTimerManager->arrListTimer4[i] ; } else if ((signed long) uDueTime < 0) { /* * Can happen if you add a timer with expires == jiffies, * or you set a timer to go off in the past */ pHead = &lpTimerManager->arrListTimer1[(lpTimerManager->uJiffies & TVR_MASK)]; } else { /* If the timeout is larger than 0xffffffff on 64-bit * architectures then we use the maximum timeout: */ if (uDueTime > 0xffffffffUL) { uDueTime = 0xffffffffUL; uExpires = uDueTime + lpTimerManager->uJiffies; } i = (uExpires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK; // i = (expires>>26) & 63 pHead = &lpTimerManager->arrListTimer5[i]; } ListTimerInsertTail(&pTmr->ltTimer, pHead) ; } static uint32 CascadeTimer(LPTIMERMANAGER lpTimerManager, struct LIST_TIMER *arrListTimer, uint32 idx) { struct LIST_TIMER listTmr, *pListTimer ; struct TIMER_NODE *pTmr ; ListTimerReplaceInit(&arrListTimer[idx], &listTmr) ; pListTimer = listTmr.pNext ; while(pListTimer != &listTmr) { pTmr = (struct TIMER_NODE *)((uint8 *)pListTimer - offsetof(struct TIMER_NODE, ltTimer)) ; pListTimer = pListTimer->pNext ; AddTimer(lpTimerManager, pTmr) ; } return idx ; } static void RunTimer(LPTIMERMANAGER lpTimerManager) { #define INDEX(N) ((lpTimerManager->uJiffies >> (TVR_BITS + (N) * TVN_BITS)) & TVN_MASK) uint32 idx, uJiffies ; struct LIST_TIMER listTmrExpire, *pListTmrExpire ; struct TIMER_NODE *pTmr ; if(NULL == lpTimerManager) return ; uJiffies = GetJiffies() ; Lock(&lpTimerManager->lock) ; while(TIME_AFTER_EQ(uJiffies, lpTimerManager->uJiffies)) { idx = lpTimerManager->uJiffies & TVR_MASK ; if (!idx && (!CascadeTimer(lpTimerManager, lpTimerManager->arrListTimer2, INDEX(0))) && (!CascadeTimer(lpTimerManager, lpTimerManager->arrListTimer3, INDEX(1))) && !CascadeTimer(lpTimerManager, lpTimerManager->arrListTimer4, INDEX(2))) CascadeTimer(lpTimerManager, lpTimerManager->arrListTimer5, INDEX(3)); //lpTimerManager->uJiffies++ ; pListTmrExpire = &listTmrExpire ; ListTimerReplaceInit(&lpTimerManager->arrListTimer1[idx], pListTmrExpire) ; pListTmrExpire = pListTmrExpire->pNext ; while(pListTmrExpire != &listTmrExpire) { pTmr = (struct TIMER_NODE *)((uint8 *)pListTmrExpire - offsetof(struct TIMER_NODE, ltTimer)) ; pListTmrExpire = pListTmrExpire->pNext ; pTmr->fnTimer(pTmr->pParam) ; // if(pTmr->uPeriod != 0) { pTmr->uExpires = lpTimerManager->uJiffies + pTmr->uPeriod ; AddTimer(lpTimerManager, pTmr) ; } else free(pTmr) ; } lpTimerManager->uJiffies++ ; } Unlock(&lpTimerManager->lock) ; } static void *ThreadRunTimer(void *pParam) { LPTIMERMANAGER pTimerMgr ; pTimerMgr = (LPTIMERMANAGER)pParam ; if(pTimerMgr == NULL) return NULL ; while(!pTimerMgr->uExitFlag) { RunTimer(pTimerMgr) ; SleepMilliseconds(1) ; } return NULL ; } void STDCALL SleepMilliseconds(uint32 uMs) { #if defined(_WIN32) || defined(_WIN64) Sleep(uMs) ; #else struct timeval tv; tv.tv_sec = 0 ; tv.tv_usec = uMs * 1000 ; select(0, NULL, NULL, NULL, &tv); #endif } //创建定时器管理器 LPTIMERMANAGER STDCALL CreateTimerManager(void) { LPTIMERMANAGER lpTimerMgr = (LPTIMERMANAGER)malloc(sizeof(TIMERMANAGER)) ; if(lpTimerMgr != NULL) { lpTimerMgr->thread = (THREAD)0 ; lpTimerMgr->uExitFlag = 0 ; InitLock(&lpTimerMgr->lock) ; lpTimerMgr->uJiffies = GetJiffies() ; InitArrayListTimer(lpTimerMgr->arrListTimer1, sizeof(lpTimerMgr->arrListTimer1)/sizeof(lpTimerMgr->arrListTimer1[0])) ; InitArrayListTimer(lpTimerMgr->arrListTimer2, sizeof(lpTimerMgr->arrListTimer2)/sizeof(lpTimerMgr->arrListTimer2[0])) ; InitArrayListTimer(lpTimerMgr->arrListTimer3, sizeof(lpTimerMgr->arrListTimer3)/sizeof(lpTimerMgr->arrListTimer3[0])) ; InitArrayListTimer(lpTimerMgr->arrListTimer4, sizeof(lpTimerMgr->arrListTimer4)/sizeof(lpTimerMgr->arrListTimer4[0])) ; InitArrayListTimer(lpTimerMgr->arrListTimer5, sizeof(lpTimerMgr->arrListTimer5)/sizeof(lpTimerMgr->arrListTimer5[0])) ; lpTimerMgr->thread = ThreadCreate(ThreadRunTimer, lpTimerMgr) ; } return lpTimerMgr ; } //删除定时器管理器 void STDCALL DestroyTimerManager(LPTIMERMANAGER lpTimerManager) { if(NULL == lpTimerManager) return ; lpTimerManager->uExitFlag = 1 ; if((THREAD)0 != lpTimerManager->thread) { ThreadJoin(lpTimerManager->thread) ; ThreadDestroy(lpTimerManager->thread) ; lpTimerManager->thread = (THREAD)0 ; } DeleteArrayListTimer(lpTimerManager->arrListTimer1, sizeof(lpTimerManager->arrListTimer1)/sizeof(lpTimerManager->arrListTimer1[0])) ; DeleteArrayListTimer(lpTimerManager->arrListTimer2, sizeof(lpTimerManager->arrListTimer2)/sizeof(lpTimerManager->arrListTimer2[0])) ; DeleteArrayListTimer(lpTimerManager->arrListTimer3, sizeof(lpTimerManager->arrListTimer3)/sizeof(lpTimerManager->arrListTimer3[0])) ; DeleteArrayListTimer(lpTimerManager->arrListTimer4, sizeof(lpTimerManager->arrListTimer4)/sizeof(lpTimerManager->arrListTimer4[0])) ; DeleteArrayListTimer(lpTimerManager->arrListTimer5, sizeof(lpTimerManager->arrListTimer5)/sizeof(lpTimerManager->arrListTimer5[0])) ; UninitLock(&lpTimerManager->lock) ; free(lpTimerManager) ; } //创建一个定时器。fnTimer回调函数地址。pParam回调函数的参数。uDueTime首次触发的超时时间间隔。uPeriod定时器循环周期,若为0,则该定时器只运行一次。 LPTIMERNODE STDCALL CreateTimer(LPTIMERMANAGER lpTimerManager, FNTIMRCALLBACK fnTimer, void *pParam, uint32 uDueTime, uint32 uPeriod) { LPTIMERNODE pTmr = NULL ; if(NULL == fnTimer || NULL == lpTimerManager) return NULL ; pTmr = (LPTIMERNODE)malloc(sizeof(TIMERNODE)) ; if(pTmr != NULL) { pTmr->uPeriod = uPeriod ; pTmr->fnTimer = fnTimer ; pTmr->pParam = pParam ; // Lock(&lpTimerManager->lock) ; pTmr->uExpires = lpTimerManager->uJiffies + uDueTime ; AddTimer(lpTimerManager, pTmr) ; Unlock(&lpTimerManager->lock) ; } return pTmr ; } //删除定时器 int32 STDCALL DeleteTimer(LPTIMERMANAGER lpTimerManager, LPTIMERNODE lpTimer) { struct LIST_TIMER *pListTmr ; if(NULL != lpTimerManager && NULL != lpTimer) { Lock(&lpTimerManager->lock) ; pListTmr = &lpTimer->ltTimer ; pListTmr->pPrev->pNext = pListTmr->pNext ; pListTmr->pNext->pPrev = pListTmr->pPrev ; free(lpTimer) ; Unlock(&lpTimerManager->lock) ; return 0 ; } else return -1 ; }
// main.c 测试例子 #include <stdio.h> #include "Timer.h" void STDCALL TimerFun(void *pParam) { LPTIMERMANAGER pMgr ; pMgr = (LPTIMERMANAGER)pParam ; printf("Timer expire! Jiffies: %lu\n", pMgr->uJiffies) ; } int main(void) { LPTIMERMANAGER pMgr ; LPTIMERNODE pTn ; pMgr = CreateTimerManager() ; CreateTimer(pMgr, TimerFun, pMgr, 2000, 0) ; pTn = CreateTimer(pMgr, TimerFun, pMgr, 4000, 1000) ; SleepMilliseconds(10001) ; DeleteTimer(pMgr, pTn) ; SleepMilliseconds(3000) ; DestroyTimerManager(pMgr) ; return 0 ; }
完整代码下载:百度云盘