APUE 习题10.5 - Implementing Software Timer

本文介绍了一种使用alarm函数实现的不精确软件定时器,探讨了随着定时器触发次数增加精度下降的问题,并指出可能存在未知的BUG和未处理的细节。
摘要由CSDN通过智能技术生成

这是我自己实现的版本,参考 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();
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值