Linux使用一个定时器实现设置任意数量定时器功能

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

本例子是为了实现使用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\n");
  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\n");
    timer_undeclare(timer_declare(&time1,0));
  }
  else
  {
    if((pid=fork())<0)
    {
      perror("fork error");
    }
    else if(pid==0)
    {
      printf("child 2\n");
      timer_undeclare(timer_declare(&time3,0));
    }
    else
    {
      printf("parent\n");
      timer_undeclare(timer_declare(&time2,0));
    }
  }
  exit(0);
}

实验结果:

parent
child 2
child 1
interrupt_hander
interrupt_hander
interrupt_hander

总结

以上所述是小编给大家介绍的Linux使用一个定时器实现设置任意数量定时器功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • linux使用select实现精确定时器详解

    在编写程序时,我们经常会用到定时器.首先看看select函数原型如下: 复制代码 代码如下: int select(int nfds, fd_set *readfds, fd_set *writefds,                  fd_set *exceptfds, struct timeval *timeout); 参数说明:slect的第一个参数nfds为fdset集合中最大描述符值加1,fdset是一个位数组,其大小限制为__FD_SETSIZE(1024),位数组的每一位代表其

  • 简单谈谈Linux内核定时器

    软件意义上的定时器最终依赖硬件定时器来实现, 内核在时钟中断发生后检测各定时器是否到期 , 到期后的定时器处理函数将作为软中断在底半部执行 .实质上,时钟中断处理程序会 换起TIMER_SOFTIRQ软中断 ,运行当前处理器上到期的所有定时器. 总结起来还是软中断的流程 a.注册软中断处理函数 /*/linux/kernel.timer.c*/ void __init init_timers(void) -->open_softirq(TIMER_SOFTIRQ, run_timer_softi

  • Linux下实现定时器Timer的几种方法总结

    定时器Timer应用场景非常广泛,在Linux下,有以下几种方法: 1,使用sleep()和usleep() 其中sleep精度是1秒,usleep精度是1微妙,具体代码就不写了.使用这种方法缺点比较明显,在Linux系统中,sleep类函数不能保证精度,尤其在系统负载比较大时,sleep一般都会有超时现象. 2,使用信号量SIGALRM + alarm() 这种方式的精度能达到1秒,其中利用了*nix系统的信号量机制,首先注册信号量SIGALRM处理函数,调用alarm(),设置定时长度,代码

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

    为什么需要这个功能,因为大多数计算机软件时钟系统通常只能有一个时钟触发一次中断.当运行多个任务时,我们会想要多个定时器 的时钟跟踪并发这样可以生成正确的时间重叠,操作系统这样做. 本例子是为了实现使用Linux下的一个定时器,实现任一数量的定时器功能. 首先我们需要一些数据类型用来描述时钟数据结构 #include <stdio.h> #include<time.h> #define TRUE 1 #define FALSE 0 #define MAX_TIMERS ... 最大时

  • Linux下安装Redis并设置相关服务

    一.简介 Redis是一个开源,先进的key-value存储,并用于构建高性能,可扩展的Web应用程序的完美解决方案. Redis从它的许多竞争继承来的三个主要特点: Redis数据库完全在内存中,使用磁盘仅用于持久性. 相比许多键值数据存储,Redis拥有一套较为丰富的数据类型. Redis可以将数据复制到任意数量的从服务器. Redis 优势  异常快速:Redis的速度非常快,每秒能执行约11万集合,每秒约81000+条记录. 支持丰富的数据类型:Redis支持最大多数开发人员已经知道像列

  • Linux环境使用crontab命令设置定时周期性执行任务【含php执行代码】

    本文实例讲述了Linux环境使用crontab命令设置定时周期性执行任务.分享给大家供大家参考,具体如下: 从linux帮助中查看crontab命令有以下参数: -u username:指定用户操作定时器 -e:编辑定时器(所有) -l:查看定时器 -r:删除定时器(从/var/spool/cron目录中删除某个用户的crontab文件,默认删除当前用户的) -i:删除定时器(删除之前给出确认提示) 使用场景1: 执行一些周期性统计的业务操作,例如每天凌晨0:00统计前一天所有业务员及各个小组的

  • linux的一个find命令配合rm删除某天前的文件方法

    语句写法:find 对应目录 -mtime +天数 -name "文件名" -exec rm -rf {} \; 例1: 将/usr/local/backups目录下所有10天前带"."的文件删除 find /usr/local/backups -mtime +10 -name "*.*" -exec rm -rf {} \; find:linux的查找命令,用户查找指定条件的文件 /usr/local/backups:想要进行清理的任意目录 -

  • linux mint 18虚拟机下设置1080P分辨率的方法

    前言:Linux mint 18发布了,但是在虚拟机下安装,发现没有1080p分辨率的选项,可能是4.4内核的原因?搜索了一下解决方法,自测可用,记录一下. 1.  创建一个shell脚本,内容如下: #!/bin/sh cvt 1920 1080 xrandr --newmode "1920x1080_60.00" 173.00 1920 2048 2248 2576 1080 1083 1088 1120 -hsync +vsync xrandr --addmode Virtual

  • Linux下root初始密码设置方法

    Ubuntu刚安装后,不能在terminal中运行su命令,因为root没有默认密码,需要手动设定. 以安装ubuntu时输入的用户名登陆,该用户在admin组中,有权限给root设定密码. 给root用户设置密码的具体步骤: 1. 打开一个terminal,然后输入下面的命令 sudo passwd root 回车后会出现让你输入原始密码,新密码和确认密码: [sudo] password for you: ---> 输入你的密码(你现在这个用户的密码) Enter new UNIX pass

  • 在Python函数中输入任意数量参数的实例

    有时候,预先不知道函数需要接受多少个实参,好在Python允许函数从调用语句中调用语句中收集任意数量的实参.在参数前加上*号. 来看一个制作披萨的函数,它需要接受很多配料,但你无法预先确定顾客要多少种配料.下面的函数只有一个形参*toppings,但不管调用语句提供了多少实参,这个形参都将他们统统收入囊中: def make_pizza(*toppings): """打印顾客点的所有配料""" print(toppings) make_pizza

  • Linux(CentOS7)安装Tomcat与设置Tomcat为开机启动项(tomcat8为例)

    安装Tomcat 下载Tomcat压缩包 Tomcat有Tomcat7,Tomcat8和Tomcat9等版本,目前企业使用较多的是Tomcat8,所以这里以Tomcat8为列 进入Tomcat8下载网址:Tomcat8下载网址https://tomcat.apache.org/download-80.cgi 点击左侧Download下的对应版本,这里我下载的是apache-tomcat-8.5.47.tar.gz,即Linux环境的压缩包 Tomcat主要有三个安装版本 tar.gz:Linux

  • 在Python中如何传递任意数量的实参的示例代码

    1 用法 在定义函数时,加上这样一个形参 "*形参名",就可以传递任意数量的实参啦: def make_tags(* tags): '''为书本打标签''' print('标签:'+str(tags)) make_tags('艺术','艺术史') 运行结果:标签:('艺术', '艺术史') 形参名 *tags 中的星号会让 Python 创建一个名为 tags 的空元组, 并将函数所收到的所有值都封装在这个元组中 . 注意:即便函数只收到一个值,也会被放入元组中. 2 联合位置实参与任

  • Python实现随机生成任意数量车牌号

    之前做课设的时候舍友遇到了需要生成500w量级车牌号的问题,于是我便写了一个随机生成车牌号的程序,希望各位采纳. 注:Python实现 import random def chepaihao(len=6): char0='京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽赣粤青藏川宁琼' char1='ABCDEFGHJKLMNPQRSTUVWXYZ'#车牌号中没有I和O,可自行百度 char2='1234567890' len0=len(char0)-1 len1 = len(char1) -

随机推荐