这是我自己实现的版本,参考 Don Libes 的 Implementing Software Timer
我是用alarm实现的,好像不太精确,还有一个版本可以设定定时器的次数,可是随着次数增多精确度就成了不可忽略的问题了。
不排除有很多BUG, 而且有些细节没考虑。
/************** timers.h *************/ /* * Title: Answer to excersice 10.5 of APUE - Software timer * By: Leon * * This implementation is similar to Don Libes's, and I modify * it according to my thought. This timer is not very precise. */ #ifndef _APUE_TIMER_ #define _APUE_TIMER_ #include <stdio.h> #include <limits.h> #include <signal.h> #include <sys/times.h> #include <time.h> #include <assert.h> #include <unistd.h> #define TRUE 1 #define FALSE 0 #define MAX_TIMERS 128 /* number of timers */ #define VERY_LONG_TIME INT_MAX /* longest time possible */ /* * no return value, the function takes * only one parameter for general purpose. */ typedef void timer_handler(void *arg); struct __apue_timer { int inuse; /* TRUE if in use */ int time_wait; /* relative time to wait */ timer_handler *handler; /* called when the timer has expired */ void *arg; /* argument of handler */ }; typedef struct __apue_timer ATIMER; /* * initialize the timers array. * This function must be called first. * returns 0 on success and -1 on error. */ int timer_init(void); /* * stop and cancel all timers. * after this function was called, don't use functions * declared here before timer_init() was called. */ void timer_cancel(void); /* * Function : decalre a timer * Parameter : time_set sets the time, timer_handler is a function pointer * points to the function wanted to call when the timer has expired. * Return value : upon successful return, the function returns a pointer points to * the declared timer. Otherwise, return NULL. */ ATIMER *timer_declare(int time_set, timer_handler *handler, void *arg); /* * Function : undeclare a timer * Parameter : a pointer points to a declared timer * Return value : returns 0 on success and -1 on error */ int timer_undeclare(ATIMER *t); /* * Function : update timers * Return value : a pointer points to the timer which will expire next * or NULL if no timers. */ ATIMER *timer_update(); /* called when a timer has expired */ void timer_out_handler(int signo); #endif /*********** timers.c *************/ #include "timers.h" /* I prefer to use array instead of linked list, which is more brevity and simpler */ static ATIMER apue_timers[MAX_TIMERS]; /* array of timers */ static ATIMER *next_timer = NULL; /* point to the next expired timer */ static volatile int time_cur = 0, time_set = 0; /* record the time */ static struct sigaction act; /* new sigaction */ static struct sigaction oldact; /* old sigaction */ static sigset_t set, old_set; static void disable_interrupt(void) { sigfillset(&set); sigdelset(&set, SIGQUIT); sigdelset(&set, SIGALRM); if(sigprocmask(SIG_SETMASK, &set, &old_set) < 0) fprintf(stderr, "sigprocmask error\n"); } static void enable_interrupt(void) { if(sigprocmask(SIG_SETMASK, &old_set, NULL) < 0) fprintf(stderr, "sigprocmask error\n"); } int timer_init(void) { int i; sigset_t mask; disable_interrupt(); /* initialize timers array */ for(i = 0; i < MAX_TIMERS; i++) apue_timers[i].inuse = FALSE; /* initialize sigaction */ sigfillset(&mask); sigdelset(&mask, SIGQUIT); sigdelset(&set, SIGALRM); act.sa_mask = mask; act.sa_handler = timer_out_handler; if(sigaction(SIGALRM, &act, &oldact) < 0) { fprintf(stderr, "sigaction error\n"); enable_interrupt(); return -1; } enable_interrupt(); return 0; } void timer_cancel(void) { int i; disable_interrupt(); alarm(0); for(i = 0; i < MAX_TIMERS; i++) apue_timers[i].inuse = FALSE; /* reset sigaction */ if(sigaction(SIGALRM, &oldact, NULL) < 0) { fprintf(stderr, "sigaction error\n"); enable_interrupt(); } enable_interrupt(); } ATIMER *timer_declare(int time_set, timer_handler *handler, void *arg) { ATIMER *new_timer; disable_interrupt(); /* find a timer not inuse */ for( new_timer = apue_timers; new_timer < &apue_timers[MAX_TIMERS] ; new_timer++) { if(!new_timer->inuse) break; } /* no timer available */ if(new_timer == &apue_timers[MAX_TIMERS]) { enable_interrupt(); return NULL; } /* found a timer */ new_timer->inuse = TRUE; new_timer->time_wait = time_set; new_timer->handler = handler; new_timer->arg = arg; /* update timers */ next_timer = timer_update(); /* set next timer */ if(!next_timer || new_timer->time_wait < next_timer->time_wait) { next_timer = new_timer; alarm(0); alarm(next_timer->time_wait); } enable_interrupt(); return new_timer; } int timer_undeclare(ATIMER *t) { disable_interrupt(); /* check parameter t */ if(t < apue_timers || t >= &apue_timers[MAX_TIMERS] || !t->inuse) { enable_interrupt(); return -1; } t->inuse = FALSE; /* reset alarm if t is next_timer */ if(t == next_timer) { alarm(0); next_timer = timer_update(); } enable_interrupt(); return 0; } ATIMER *timer_update() { int decrement; int flag = FALSE; ATIMER *t; ATIMER temp_timer = { 0, VERY_LONG_TIME, NULL }; disable_interrupt(); /* update time */ if(time_set == 0) { time_set = times(NULL); decrement = 0; } else { time_cur = times(NULL); /* get decrement and convert to seconds */ decrement = (time_cur - time_set) / sysconf(_SC_CLK_TCK); /* printf("decrement = %d\n", decrement);/* !!for debug!! */ assert(decrement >= 0); /* someting may happen */ time_set = time_cur; /* reset time_set */ } /* reset next_timer */ next_timer = &temp_timer; /* update timers and get next timer */ for(t = apue_timers; t < &apue_timers[MAX_TIMERS]; t++) { if(t->inuse) { if(decrement < t->time_wait) t->time_wait -= decrement; if(t->time_wait < next_timer->time_wait) next_timer = t; flag = TRUE; } } /* reset alarm */ alarm(0); if(flag) { alarm(next_timer->time_wait); } else { next_timer = NULL; time_cur = time_set = 0; } enable_interrupt(); return next_timer; } void timer_out_handler(int signo) { /* call user hanler */ next_timer->handler(next_timer->arg); /* update timers */ next_timer->inuse = FALSE; next_timer = timer_update(); }