应用层使用timer可以启动多个timer(每个timer管理一个目标时间),也可启用一个timer来管理多个目标时间。
多个timer时每个timer占用一部分空间,且存在多个timer同时到期的先后顺序问题(未多考虑,是否有问题待确定),可采用单个timer管理程序所有定时事件,即如何实现序列化的timer。
涉及到链表(记录多个目标时间的到期时间),信号处理函数(在SIG_ALAM函数中处理timer事件,并启动下一个timer时间点)。
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <signal.h> #include <time.h> #include <sys/time.h> #include <string.h> #include "list.h" #define TIMERID_FIRST 100 typedef void (*stimer_func)(void *arg); struct signal_timer{ struct list_head list_timer; int id; struct timeval itv; struct timeval atv; stimer_func stimer_handler; void *arg; }; int g_id; struct list_head g_signal_list; void alarm_handler(int sig); int timer_init(void); void timer_destroy(void); // Return: id >0, in case of success; -1 in case of error. int timer_add(long sec, long usec, void (*stimer_func)(void *arg), void *arg); void timer_del(int id, int is_free); static long long diff_time(struct timeval *t1, struct timeval *t2) { long long t1_usec = (t1->tv_sec * 1000 * 1000 + t1->tv_usec); long long t2_usec = (t2->tv_sec * 1000 * 1000 + t2->tv_usec); return (t1_usec - t2_usec); } static int max_time(struct timeval *t1, struct timeval *t2) { if(t1->tv_sec < t2->tv_sec){ return -1; } else if(t1->tv_sec > t2->tv_sec){ return 1; } else { if(t1->tv_usec < t2->tv_usec){ return -1; } else if(t1->tv_usec > t2->tv_usec){ return 1; } else { return 0; } } } #define min_time(t1, t2) (((t1).tv_sec < (t2).tv_sec) || (((t1).tv_sec == (t2).tv_sec) && ((t1).tv_usec < (t2).tv_usec))) #define MAX_USEC 999999 #define TV_MINUS(t1, t2, target) if((t1).tv_usec >= (t2).tv_usec){ (target).tv_sec = (t1).tv_sec - (t2).tv_sec; (target).tv_usec = (t1).tv_usec - (t2).tv_usec; } else { (target).tv_sec = (t1).tv_sec - (t2).tv_sec - 1; (target).tv_usec = (t1).tv_usec + (MAX_USEC - (t2).tv_usec); } void alarm_handler(int sig) { struct timespec ts; struct timeval tv; struct timeval tv_min, *ptv_min; struct itimerval it; struct signal_timer *pstimer = NULL, *next= NULL; int ret = 0; if(list_empty(&g_signal_list)) return ; // pstimer = list_first_entry(&g_signal_list, struct signal_timer, list_timer); // ptv_min = &(pstimer->atv); ptv_min = &tv_min; clock_gettime(CLOCK_MONOTONIC_RAW, &ts); tv.tv_sec = ts.tv_sec; tv.tv_usec = ts.tv_nsec / 1000; // printf("now time: %ld:%ld ", tv.tv_sec, tv.tv_usec); tv_min = tv; tv_min.tv_sec += 1000; //default 1000s once // two methods: sequence the timer list in case of more timers, // not sequence in case of fewer timers. list_for_each_entry_safe(pstimer, next, &g_signal_list, list_timer){ // printf("timerid:%d, aim time: %ld:%ld ", pstimer->id, pstimer->atv.tv_sec, pstimer->atv.tv_usec); if(max_time(&pstimer->atv, &tv) <= 0){ if(pstimer->stimer_handler != NULL){ pstimer->stimer_handler(pstimer->arg); } // only operation once, when overflow more times. do{ pstimer->atv.tv_sec += pstimer->itv.tv_sec; pstimer->atv.tv_usec += pstimer->itv.tv_usec; } while(max_time(&pstimer->atv, &tv) < 0); // } else { // break; } // get next itimer if(min_time(pstimer->atv, *ptv_min)){ ptv_min = &(pstimer->atv); } } memset(&it, 0, sizeof(it)); // it.it_value.tv_sec = ptv_min->tv_sec - tv.tv_sec; // it.it_value.tv_usec = ptv_min->tv_usec - tv.tv_usec; TV_MINUS(*ptv_min, tv, it.it_value); ret = setitimer(ITIMER_REAL, &it, NULL); if(ret < 0){ perror("setitimer"); } printf("process SIGALRM, next time is %ld:%ld ", it.it_value.tv_sec, it.it_value.tv_usec); } int timer_add(long sec, long usec, void (*stimer_func)(void *arg), void *arg) { struct signal_timer *pstimer = (struct signal_timer*)malloc(sizeof(struct signal_timer)); struct timespec ts; memset(pstimer, 0, sizeof(*pstimer)); pstimer->id = g_id++; pstimer->itv.tv_sec = sec; pstimer->itv.tv_usec = usec; pstimer->stimer_handler = stimer_func; if(arg){ pstimer->arg = arg; }else { pstimer->arg = pstimer; } list_add(&(pstimer->list_timer), &g_signal_list); clock_gettime(CLOCK_MONOTONIC_RAW, &ts); pstimer->atv.tv_sec += ts.tv_sec; pstimer->atv.tv_usec += ts.tv_nsec / 1000; return pstimer->id; } void timer_del(int id, int is_free) { struct signal_timer *pstimer = NULL; list_for_each_entry(pstimer, &g_signal_list, list_timer){ if(pstimer->id == id){ list_del(&(pstimer->list_timer)); printf("----delete timerid is %d ", pstimer->id); if(is_free){ free(pstimer); } break; } } } int timer_init(void) { INIT_LIST_HEAD(&g_signal_list); struct sigaction act; int ret = 0; // memset(&act, 0, sizeof(act)); sigemptyset(&act.sa_mask); act.sa_handler = alarm_handler; act.sa_flags = 0; ret = sigaction(SIGALRM, &act, NULL); if(ret < 0){ perror("sigaction"); return -1; } g_id = TIMERID_FIRST ; return 0; } void timer_destroy(void) { struct signal_timer *pstimer = NULL; while(! list_empty((&g_signal_list)->next)){ list_del((&g_signal_list)->next); pstimer = container_of((&g_signal_list)->next, struct signal_timer, list_timer); free(pstimer); } } void timer_printf(void *arg) { if(arg){ struct signal_timer *pstimer = (struct signal_timer*)arg; printf("timerid:%d, aim time: %ld:%ld ", pstimer->id, pstimer->atv.tv_sec, pstimer->atv.tv_usec); } else { printf("timerid is %d ", g_id); } } int main(int argc, char **argv) { int tid1 = 0, tid2 = 0, tid3 = 0; int i = 0; signal(SIGPIPE, SIG_IGN); timer_init(); tid1 = timer_add(2, 0, timer_printf, NULL); tid2 = timer_add(3, 0, timer_printf, NULL); tid3 = timer_add(5, 0, timer_printf, NULL); alarm(1); while(1){ sleep(1); printf("sleep 1s "); i++; if(i%3 == 0) timer_del(tid1, true); if(i%7 == 0) timer_del(tid2, true); if(i%9 == 0) timer_del(tid3, true); } timer_destroy(); return 0; }
运行:
timerid:102, aim time: 468486:907381 timerid:101, aim time: 468486:907380 timerid:100, aim time: 468486:907380 process SIGALRM, next time is 0:999678 sleep 1s timerid:100, aim time: 468488:907380 process SIGALRM, next time is 0:998577 sleep 1s timerid:101, aim time: 468489:907380 process SIGALRM, next time is 0:999371 sleep 1s ----delete timerid is 100 process SIGALRM, next time is 0:999003 sleep 1s timerid:102, aim time: 468491:907381 process SIGALRM, next time is 0:998901 sleep 1s timerid:101, aim time: 468492:907380 process SIGALRM, next time is 2:998319 sleep 1s sleep 1s ----delete timerid is 101 sleep 1s process SIGALRM, next time is 0:999768 sleep 1s ----delete timerid is 102 sleep 1s sleep 1s sleep 1s sleep 1s