用户态下多定时器的实现

参考书《网络编程与分层协议设计基于Linux平台实现》ISBN 978-7-111-35052-1

用链表实现多个定时器

snmp_aparm.h

#ifndef SNMP_ALARM_H
#define SNMP_ALARM_H

#define RETSIGTYPE void

typedef void (SNMPAlarmCallback) (unsigned int clientreg, void *clientarg);

/*
 * alarm flags 
 */
#define SA_REPEAT 0x01          /* keep repeating every X seconds */

struct snmp_alarm {
    struct timeval t;
    unsigned int flags;
    unsigned int clientreg;
    struct timeval t_last;
    struct timeval t_next;
    void *clientarg;
    SNMPAlarmCallback *thecallback;
    struct snmp_alarm *next;
};

/*
 * the ones you should need 
 */
void snmp_alarm_unregister(unsigned int clientreg);
void snmp_alarm_unregister_all(void);
unsigned int snmp_alarm_register(unsigned int when,
                                 unsigned int flags,
                                 SNMPAlarmCallback * thecallback,
                                 void *clientarg);

unsigned int snmp_alarm_register_hr(struct timeval t,
                                    unsigned int flags,
                                    SNMPAlarmCallback * cb, void *cd);

/*
 * the ones you shouldn't 
 */
int init_alarm_post_config();
void sa_update_entry(struct snmp_alarm *alrm);
struct snmp_alarm *sa_find_next(void);
void run_alarms(void);
RETSIGTYPE alarm_handler(int a);
void set_an_alarm(void);
int get_next_alarm_delay_time(struct timeval *delta);

#endif
snmp_alarm.c

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>

#include <sys/time.h>
#include <time.h>

#include "snmp_alarm.h"

#define SNMP_MALLOC_STRUCT(s)   (struct s *) calloc(1, sizeof(struct s))

static struct snmp_alarm *thealarms = NULL;
static int start_alarms = 0;
static unsigned int regnum = 1;

int init_alarm_post_config()
{
    start_alarms = 1;
    set_an_alarm();
    return 0;
}

void sa_update_entry(struct snmp_alarm *a)
{
    if(a->t_last.tv_sec == 0 && a->t_last.tv_usec == 0) {
        struct timeval t_now;

        /*
         * Never been called yet, call time `t' from now.  
         */
        gettimeofday(&t_now, NULL);

        a->t_last.tv_sec = t_now.tv_sec;
        a->t_last.tv_usec = t_now.tv_usec;

        a->t_next.tv_sec = t_now.tv_sec + a->t.tv_sec;
        a->t_next.tv_usec = t_now.tv_usec + a->t.tv_usec;

        while(a->t_next.tv_usec >= 1000000) {
            a->t_next.tv_usec -= 1000000;
            a->t_next.tv_sec += 1;
        }
    }
    else if(a->t_next.tv_sec == 0 && a->t_next.tv_usec == 0) {
        /*
         * We've been called but not reset for the next call.  
         */
        if(a->flags & SA_REPEAT) {
            if(a->t.tv_sec == 0 && a->t.tv_usec == 0) {
                printf("update_entry: illegal interval specified\n");
                snmp_alarm_unregister(a->clientreg);
                return;
            }

            a->t_next.tv_sec = a->t_last.tv_sec + a->t.tv_sec;
            a->t_next.tv_usec = a->t_last.tv_usec + a->t.tv_usec;

            while(a->t_next.tv_usec >= 1000000) {
                a->t_next.tv_usec -= 1000000;
                a->t_next.tv_sec += 1;
            }
        }
        else {
            /*
             * Single time call, remove it.  
             */
            snmp_alarm_unregister(a->clientreg);
        }
    }
}

/**
 * This function removes the callback function from a list of registered
 * alarms, unregistering the alarm.
 *
 * @param clientreg is a unique unsigned integer representing a registered
 *        alarm which the client wants to unregister.
 *
 * @return void
 *
 * @see snmp_alarm_register
 * @see snmp_alarm_register_hr
 * @see snmp_alarm_unregister_all
 */
void snmp_alarm_unregister(unsigned int clientreg)
{
    struct snmp_alarm *sa_ptr, **prevNext = &thealarms;

    for(sa_ptr = thealarms;
        sa_ptr != NULL && sa_ptr->clientreg != clientreg;
        sa_ptr = sa_ptr->next) {
        prevNext = &(sa_ptr->next);
    }

    if(sa_ptr != NULL) {
        *prevNext = sa_ptr->next;
        printf("unregistered alarm %d\n", sa_ptr->clientreg);
        /*
         * Note:  do not free the clientarg, its the clients responsibility 
         */
        free(sa_ptr);
    }
    else {
        printf("no alarm %d to unregister\n", clientreg);
    }
}

/**
 * This function unregisters all alarms currently stored.
 *
 * @return void
 *
 * @see snmp_alarm_register
 * @see snmp_alarm_register_hr
 * @see snmp_alarm_unregister
 */
void snmp_alarm_unregister_all(void)
{
    struct snmp_alarm *sa_ptr, *sa_tmp;

    for(sa_ptr = thealarms; sa_ptr != NULL; sa_ptr = sa_tmp) {
        sa_tmp = sa_ptr->next;
        free(sa_ptr);
    }
    printf("ALL alarms unregistered\n");
    thealarms = NULL;
}

struct snmp_alarm *sa_find_next(void)
{
    struct snmp_alarm *a, *lowest = NULL;

    for(a = thealarms; a != NULL; a = a->next) {
        if(lowest == NULL) {
            lowest = a;
        }
        else if(a->t_next.tv_sec == lowest->t_next.tv_sec) {
            if(a->t_next.tv_usec < lowest->t_next.tv_usec) {
                lowest = a;
            }
        }
        else if(a->t_next.tv_sec < lowest->t_next.tv_sec) {
            lowest = a;
        }
    }
    return lowest;
}

struct snmp_alarm *sa_find_specific(unsigned int clientreg)
{
    struct snmp_alarm *sa_ptr;

    for(sa_ptr = thealarms; sa_ptr != NULL; sa_ptr = sa_ptr->next) {
        if(sa_ptr->clientreg == clientreg) {
            return sa_ptr;
        }
    }
    return NULL;
}

void run_alarms(void)
{
    int done = 0;
    struct snmp_alarm *a = NULL;
    unsigned int clientreg;
    struct timeval t_now;

    /*
     * Loop through everything we have repeatedly looking for the next thing to
     * call until all events are finally in the future again.  
     */

    while(!done) {
        if((a = sa_find_next()) == NULL) {
            return;
        }

        gettimeofday(&t_now, NULL);

        if((a->t_next.tv_sec < t_now.tv_sec) ||
           ((a->t_next.tv_sec == t_now.tv_sec) &&
            (a->t_next.tv_usec < t_now.tv_usec))) {
            clientreg = a->clientreg;
            printf("run alarm %d\n", clientreg);
            (*(a->thecallback)) (clientreg, a->clientarg);
            printf("alarm %d completed\n", clientreg);

            if((a = sa_find_specific(clientreg)) != NULL) {
                a->t_last.tv_sec = t_now.tv_sec;
                a->t_last.tv_usec = t_now.tv_usec;
                a->t_next.tv_sec = 0;
                a->t_next.tv_usec = 0;
                sa_update_entry(a);
            }
            else {
                printf("alarm %d deleted itself\n", clientreg);
            }
        }
        else {
            done = 1;
        }
    }
}



RETSIGTYPE alarm_handler(int a)
{
    run_alarms();
    set_an_alarm();
}



int get_next_alarm_delay_time(struct timeval *delta)
{
    struct snmp_alarm *sa_ptr;
    struct timeval t_diff, t_now;

    sa_ptr = sa_find_next();

    if(sa_ptr) {
        gettimeofday(&t_now, 0);

        if((t_now.tv_sec > sa_ptr->t_next.tv_sec) ||
           ((t_now.tv_sec == sa_ptr->t_next.tv_sec) &&
            (t_now.tv_usec > sa_ptr->t_next.tv_usec))) {
            /*
             * Time has already passed.  Return the smallest possible amount of
             * time.  
             */
            delta->tv_sec = 0;
            delta->tv_usec = 1;
            return sa_ptr->clientreg;
        }
        else {
            /*
             * Time is still in the future.  
             */
            t_diff.tv_sec = sa_ptr->t_next.tv_sec - t_now.tv_sec;
            t_diff.tv_usec = sa_ptr->t_next.tv_usec - t_now.tv_usec;

            while(t_diff.tv_usec < 0) {
                t_diff.tv_sec -= 1;
                t_diff.tv_usec += 1000000;
            }

            delta->tv_sec = t_diff.tv_sec;
            delta->tv_usec = t_diff.tv_usec;
            return sa_ptr->clientreg;
        }
    }

    /*
     * Nothing Left.  
     */
    return 0;
}


void set_an_alarm(void)
{
    struct timeval delta;
    int nextalarm = get_next_alarm_delay_time(&delta);

    /*
     * We don't use signals if they asked us nicely not to.  It's expected
     * they'll check the next alarm time and do their own calling of
     * run_alarms().  
     */

    if(nextalarm) {
        struct itimerval it;

        it.it_value.tv_sec = delta.tv_sec;
        it.it_value.tv_usec = delta.tv_usec;
        it.it_interval.tv_sec = 0;
        it.it_interval.tv_usec = 0;

        signal(SIGALRM, alarm_handler);
        setitimer(ITIMER_REAL, &it, NULL);
        printf("schedule alarm %d in %d.%03d seconds\n",
               nextalarm, (int) delta.tv_sec,
               (int) (delta.tv_usec / 1000));

    }
    else {
        printf("no alarms found to schedule\n");
    }
}


/**
 * This function registers function callbacks to occur at a speciifc time
 * in the future.
 *
 * @param when is an unsigned integer specifying when the callback function
 *             will be called in seconds.
 *
 * @param flags is an unsigned integer that specifies how frequent the callback
 *        function is called in seconds.  Should be SA_REPEAT or 0.  If  
 *        flags  is  set with SA_REPEAT, then the registered callback function
 *        will be called every SA_REPEAT seconds.  If flags is 0 then the 
 *        function will only be called once and then removed from the 
 *        registered alarm list.
 *
 * @param thecallback is a pointer SNMPAlarmCallback which is the callback 
 *        function being stored and registered.
 *
 * @param clientarg is a void pointer used by the callback function.  This 
 *        pointer is assigned to snmp_alarm->clientarg and passed into the
 *        callback function for the client's specifc needs.
 *
 * @return Returns a unique unsigned integer(which is also passed as the first 
 *        argument of each callback), which can then be used to remove the
 *        callback from the list at a later point in the future using the
 *        snmp_alarm_unregister() function.  If memory could not be allocated
 *        for the snmp_alarm struct 0 is returned.
 *
 * @see snmp_alarm_unregister
 * @see snmp_alarm_register_hr
 * @see snmp_alarm_unregister_all
 */
unsigned int
snmp_alarm_register(unsigned int when, unsigned int flags,
                    SNMPAlarmCallback * thecallback, void *clientarg)
{
    struct snmp_alarm **sa_pptr;

    if(thealarms != NULL) {
        for(sa_pptr = &thealarms; (*sa_pptr) != NULL;
            sa_pptr = &((*sa_pptr)->next)) ;
    }
    else {
        sa_pptr = &thealarms;
    }

    *sa_pptr = SNMP_MALLOC_STRUCT(snmp_alarm);
    if(*sa_pptr == NULL)
        return 0;

    if(0 == when) {
        (*sa_pptr)->t.tv_sec = 0;
        (*sa_pptr)->t.tv_usec = 1;
    }
    else {
        (*sa_pptr)->t.tv_sec = when;
        (*sa_pptr)->t.tv_usec = 0;
    }
    (*sa_pptr)->flags = flags;
    (*sa_pptr)->clientarg = clientarg;
    (*sa_pptr)->thecallback = thecallback;
    (*sa_pptr)->clientreg = regnum++;
    (*sa_pptr)->next = NULL;
    sa_update_entry(*sa_pptr);

    printf("registered alarm %d, t = %d.%03d, flags=0x%02x\n",
           (*sa_pptr)->clientreg, (int) (*sa_pptr)->t.tv_sec,
           (int) ((*sa_pptr)->t.tv_usec / 1000), (*sa_pptr)->flags);

    if(start_alarms){
        set_an_alarm();
    }

    return (*sa_pptr)->clientreg;
}


/**
 * This function offers finer granularity as to when the callback 
 * function is called by making use of t->tv_usec value forming the 
 * "when" aspect of snmp_alarm_register().
 *
 * @param t is a timeval structure used to specify when the callback 
 *        function(alarm) will be called.  Adds the ability to specify
 *        microseconds.  t.tv_sec and t.tv_usec are assigned
 *        to snmp_alarm->tv_sec and snmp_alarm->tv_usec respectively internally.
 *        The snmp_alarm_register function only assigns seconds(it's when 
 *        argument).
 *
 * @param flags is an unsigned integer that specifies how frequent the callback
 *        function is called in seconds.  Should be SA_REPEAT or NULL.  If  
 *        flags  is  set with SA_REPEAT, then the registered callback function
 *        will be called every SA_REPEAT seconds.  If flags is NULL then the 
 *        function will only be called once and then removed from the 
 *        registered alarm list.
 *
 * @param cb is a pointer SNMPAlarmCallback which is the callback 
 *        function being stored and registered.
 *
 * @param cd is a void pointer used by the callback function.  This 
 *        pointer is assigned to snmp_alarm->clientarg and passed into the
 *        callback function for the client's specifc needs.
 *
 * @return Returns a unique unsigned integer(which is also passed as the first 
 *        argument of each callback), which can then be used to remove the
 *        callback from the list at a later point in the future using the
 *        snmp_alarm_unregister() function.  If memory could not be allocated
 *        for the snmp_alarm struct 0 is returned.
 *
 * @see snmp_alarm_register
 * @see snmp_alarm_unregister
 * @see snmp_alarm_unregister_all
 */
unsigned int
snmp_alarm_register_hr(struct timeval t, unsigned int flags,
                       SNMPAlarmCallback * cb, void *cd)
{
    struct snmp_alarm **s = NULL;

    for(s = &(thealarms); *s != NULL; s = &((*s)->next)) ;

    *s = SNMP_MALLOC_STRUCT(snmp_alarm);
    if(*s == NULL) {
        return 0;
    }

    (*s)->t.tv_sec = t.tv_sec;
    (*s)->t.tv_usec = t.tv_usec;
    (*s)->flags = flags;
    (*s)->clientarg = cd;
    (*s)->thecallback = cb;
    (*s)->clientreg = regnum++;
    (*s)->next = NULL;

    sa_update_entry(*s);

    printf("registered alarm %d, t = %d.%03d, flags=0x%02x\n",
           (*s)->clientreg, (int) (*s)->t.tv_sec,
           (int) ((*s)->t.tv_usec / 1000), (*s)->flags);

    if(start_alarms) {
        set_an_alarm();
    }

    return (*s)->clientreg;
}

/**  @} */

void fun1(unsigned int clientreg, void *clientarg)
{
    fprintf(stderr,"fun:%s clientreg:%d\n",__FUNCTION__,clientreg);
}

void fun2(unsigned int clientreg, void *clientarg)
{
    fprintf(stderr,"fun:%s clientreg:%d\n",__FUNCTION__,clientreg);
}


//使用示例 很简单
int main()
{
    start_alarms=1;
	set_an_alarm();
    snmp_alarm_register(1, SA_REPEAT,fun1,NULL);
    snmp_alarm_register(5, SA_REPEAT,fun2,NULL);
    for(;;){
        pause();
    }
    return 0;
}



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值