• LINUX使用一个定时器实现设置任意数量定时器


    本例子参考 Don Libes的Title: Implementing Software Timers例子改写

    为什么需要这个功能,因为大多数计算机软件时钟系统通常只能有一个时钟触发一次中断。当运行多个任务时,我们会想要多个定时器 的时钟跟踪并发这样可以生成正确的时间重叠,操作系统这样做。

    本例子是为了实现使用Linux下的一个定时器,实现任一数量的定时器功能。

    首先我们需要一些数据类型用来描述时钟数据结构

    #include <stdio.h>
    
    #include<time.h>
    
    #define TRUE 1
    #define FALSE    0
    
    #define MAX_TIMERS    ...    最大时钟数量
    typedef timerval TIME; 定义时间类型
    #define VERY_LONG_TIME    ... 最大时间长度
    
    struct timer {
    int inuse;    时钟是否可用
    TIME time; 定时时间长度
    char *event; 是否超时
    } timers[MAX_TIMERS];    /* set of timers */

    每个定时器都以这个数据结构来描述,第一个成员用来描述时钟是否正在使用,第二个成员是这个定时器的定时时间,第三个成员是是一个指针,*event初始化应该为0,当他被置为1,我们知道这个定时器已经超时了,和他相关的任务可以执行。

    接下来是定时器数组的初始化,这里将每个时钟inuse成员设置为FALSE,表示时钟不可用。

    void
    timers_init() {
    struct timer *t;
    
    for (t=timers;t<&timers[MAX_TIMERS];t++)
    t->inuse = FALSE;
    }

    现在开始是结构实现部分

    首先写到的timer_undeclare这个函数,这个函数与后面的timer_declare相对立。主要作用是清除一个定时器。

    有很多方法可以用来保存定时器的定时记录。没有复杂时钟硬件的机器通常在每一个时钟周期处理一个中断处理程序。然后软件就在处理程序中获取系统时间,然后判断是否设置的定时器超时。

    很多比较聪明的机器可以在硬件中设置定时时间,一旦时间超时,就触发一个硬件中断。这同样适用与软件中断。

    他们通过一个 定义一个time_now来记录当前的系统时间,volatile告诉机器每次从寄存器取值,防止数据被系统优化。

    volatile TIME time_now

    接下来定义一系列数据来记录 timer_next 指接下来要我们想要计时的定时器。time_timer_set保存最后一次获取的系统时间。

    struct timer *timer_next = NULL;/* timer we expect to run down next */
    TIME time_timer_set;    /* time when physical timer was set */
    
    //取消一个定时器
    void timer_undeclare(struct timer *t)
    {
      disable_interrupts();
      if(!t->inuse)
      {
        enable_interrupts();
        return ;
      }
      t->inuse=0;
      if(t==timer_next)
      {
        if(time(&time_now)<0)
        perror("time error");
        timers_update(time_now-time_timer_set);    
        if(timer_next)
        {
          start_physical_timer(&timer_next->time);
          time_timer_set=time_now;
        }
      }
      enable_interrupts();
    
    }

    timer_undeclare作用为取消一个定时器。首先让中断失效,这很重要,因为时钟数据结构数据是在各进程中共享的,是可以在其他中断中被修改的,为了防止不必要的错我,这个取消操作应该为一个原子操作。开始我们先检测是否这个时钟已经无效了。如果有效,则设置inuse使其失效。如果我们要取消的定时器正好是下一个期望等待的定时器。那我们要重新指定下一个期望等待的定时器。在此之前所有定时器都要更新一下前一个定时器已经走过的时间。

    接下来我们看到timers_update(time_t ti)函数

    //更新定时器表时间
    void timers_update(time_t time)
    {
      static struct timer timer_last={
      0,
      {},
      NULL
      };
      timer_last.time.tv_sec=10;
    
     
    
      struct timer *t;
    
     
    
      timer_next=&timer_last;
    
     
    
      for(t=timers;t<&timers[MAX_TIMERS];t++)
      {
        if(t->inuse)
        {
          if(time<t->time.tv_sec){
          t->time.tv_sec-=time;
          if(t->time.tv_sec<
          timer_next->time.tv_sec)
            timer_next=t;
          }else
          {
            *(t->event)=1;
            t->inuse=0;
          }
        }
      }
      if(!timer_next->inuse)timer_next=0;
    }
    
     

    此函数作用是更新所有有效定时器的时间长,同时将timer_next指向当前延时时间最短的一个定时器。如没有定时器,则将timer_next设置为空。

    timer_declare 加入一个定时器 

    struct timer * timer_declare(TIME *ti,char *event)
    {
      struct timer *t;
      disable_interrupts();
    
      for(t=timers;t<&timers[MAX_TIMERS];t++)
      {
        if(!t->inuse)break;
      }
    
      if(t==&timers[MAX_TIMERS])
      {
        enable_interrupts();
        return 0;
      }
    
      t->event=event;
      t->time.tv_sec=ti->tv_sec;
      t->time.tv_usec=ti->tv_usec;
      if(!timer_next)
      {
    
        if(time(&time_now)<0)
        perror("time() error");
        time_timer_set=time_now;
        start_physical_timer(&((timer_next=t)->time));    
      }else if((ti->tv_sec+time_now)<(
      timer_next->time.tv_sec+time_timer_set))
      {
        if(time(&time_now)<0)
        perror("time error");
        timers_update(time_now-time_timer_set);
        time_timer_set=time_now;
        start_physical_timer(&((timer_next=t)->time));
      }else
      {
    
      }
      t->inuse=1;
      enable_interrupts();
      return t;
    }

    首先找到一个可用的定时器表项,设置相关参数。

    接下来判断如果timer_next为空,那么说明定时器表项没有定时器需要定时,那我们直接将timer_next指向新加入定时器,开始计时。

    如果新加入定时器需要延时时间比当前正在延时的定时器的剩余时间还要短,则更新定时器表,并计时当前加入的定时器。

    处理完当前定时器事件后,将新加入的定时器的inuse置1.

    接下来是定时器中断处理函数

    //定时器中断处理函数
    void timer_interrupt_hander(int signo)
    {
      printf("interrupt_hander
    ");
      if(time(&time_now)<0)
        perror("time() error");
      timers_update(time_now-time_timer_set);
    
      if(timer_next)
      {
        time_timer_set=time_now;
        start_physical_timer(&timer_next->time);    
      }
    }

    这里我们打印一串字符来证明定时器时间的触发,首先要做的先更新定时器表,然后将time_timer_set设置成当前系统时间,继续进行下一个定时器事件,直到所有定时器都处理完毕。

    接下来几个是LINUX系统相关函数

    //失效定时器中断
    void disable_interrupts()
    {
    sigset_t new_mask;
    
    sigemptyset(&new_mask);
    sigaddset(&new_mask,SIGALRM);
    
    if(sigprocmask(SIG_BLOCK,&new_mask,NULL)<0)
    perror("SIG_BLOCK error");
    }
    
    //使能定时器中断
    void enable_interrupts()
    {
    sigset_t new_mask;
    
    sigemptyset(&new_mask);
    sigaddset(&new_mask,SIGALRM);
    if(sigprocmask(SIG_UNBLOCK,&new_mask,NULL)<0)
    perror("SIG_UNBLOCK error");
    }
    
    //开启一个定时器工作
    void start_physical_timer(TIME* time)
    {
    if(signal(SIGALRM,timer_interrupt_hander)==SIG_ERR)
    perror("signal error");    
    struct itimerval new_value;
    sigset_t zero_mask;
    sigemptyset(&zero_mask);
    
    new_value.it_value.tv_sec=time->tv_sec;
    new_value.it_value.tv_usec=time->tv_usec;
    new_value.it_interval.tv_sec=0;
    new_value.it_interval.tv_usec=0;
    setitimer(ITIMER_REAL,&new_value,NULL);
    sigsuspend(&zero_mask);
    }

    主函数测试部分

    #include<stdio.h>
    #include<signal.h>
    #include"multtime.h"
    #include<stdlib.h>
    #include<unistd.h>
    
    
    int main()
    {
        pid_t pid;
        TIME time1,time2,time3;
        time1.tv_sec=6;
        time1.tv_usec=0;
        
        time2.tv_sec=4;
        time2.tv_usec=0;
    
        time3.tv_sec=2;
        time3.tv_usec=0;
        timer_init();
        if((pid=fork())<0)
        {
            perror("fork() error");
        }
        else if(pid==0)
        {
            printf("child 1
    ");
            timer_undeclare(timer_declare(&time1,0));
        }
        else 
        {    
            if((pid=fork())<0)
            {
                perror("fork error");
            }
            else if(pid==0)
            {
                printf("child 2
    ");
                timer_undeclare(timer_declare(&time3,0));
            }
            else
            {
                printf("parent
    ");
                timer_undeclare(timer_declare(&time2,0));
            }
        }
        
    
        exit(0); 
    }

    实验结果:

    parent
    child 2
    child 1
    interrupt_hander
    interrupt_hander
    interrupt_hander

    本人初学LINUX是个菜鸟,以自己的理解写的,待深入学习后再完善改进。

     

  • 相关阅读:
    jstack使用教程
    频繁fullgc排查
    ubuntu添加ubuntu make
    Spring属性编辑器详解
    mysql 查看触发器,删除触发器
    mongodb启动不了:提示错误信息为 child process failed, exited with error number 100
    RedHat7 防火墙设置以及端口设置
    linux 设置静态IP方法
    linux 安装mongo
    mongo 介绍
  • 原文地址:https://www.cnblogs.com/a-lai/p/7298463.html
Copyright © 2020-2023  润新知