Linux使用一个定时器实现设置任意数量定时器功能【转】
转自:https://www.jb51.net/article/120748.htm
为什么需要这个功能,因为大多数计算机软件时钟系统通常只能有一个时钟触发一次中断。当运行多个任务时,我们会想要多个定时器 的时钟跟踪并发这样可以生成正确的时间重叠,操作系统这样做。 本例子是为了实现使用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=;
if(t==timer_next)
{
if(time(&time_now)<)
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={
,
{},
NULL
};
timer_last.time.tv_sec=;
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)=;
t->inuse=;
}
}
}
if(!timer_next->inuse)timer_next=;
} 此函数作用是更新所有有效定时器的时间长,同时将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 ;
}
t->event=event;
t->time.tv_sec=ti->tv_sec;
t->time.tv_usec=ti->tv_usec;
if(!timer_next)
{
if(time(&time_now)<)
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)<)
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=;
enable_interrupts();
return t;
} 首先找到一个可用的定时器表项,设置相关参数。 接下来判断如果timer_next为空,那么说明定时器表项没有定时器需要定时,那我们直接将timer_next指向新加入定时器,开始计时。 如果新加入定时器需要延时时间比当前正在延时的定时器的剩余时间还要短,则更新定时器表,并计时当前加入的定时器。 在处理完当前定时器事件后,将新加入的定时器的inuse置1. 接下来是定时器中断处理函数 //定时器中断处理函数
void timer_interrupt_hander(int signo)
{
printf("interrupt_hander\n");
if(time(&time_now)<)
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)<)
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)<)
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=;
new_value.it_interval.tv_usec=;
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=;
time1.tv_usec=;
time2.tv_sec=;
time2.tv_usec=;
time3.tv_sec=;
time3.tv_usec=;
timer_init();
if((pid=fork())<)
{
perror("fork() error");
}
else if(pid==)
{
printf("child 1\n");
timer_undeclare(timer_declare(&time1,));
}
else
{
if((pid=fork())<)
{
perror("fork error");
}
else if(pid==)
{
printf("child 2\n");
timer_undeclare(timer_declare(&time3,));
}
else
{
printf("parent\n");
timer_undeclare(timer_declare(&time2,));
}
}
exit();
} 实验结果: parent
child
child
interrupt_hander
interrupt_hander
interrupt_hander 总结 以上所述是小编给大家介绍的Linux使用一个定时器实现设置任意数量定时器功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!
Linux使用一个定时器实现设置任意数量定时器功能【转】的更多相关文章
- JS---动画函数封装:设置任意的一个元素,移动到指定的目标位置
动画函数封装:设置任意的一个元素,移动到指定的目标位置 <!DOCTYPE html> <html lang="en"> <head> < ...
- 请写一个php函数,可以接受任意数量的参数
请写一个php函数,可以接受任意数量的参数 这是一道面试题.怎么写这个函数呢? function fun(......) { } ----------------------------------- ...
- Python 构造一个可接受任意数量参数的函数
为了能让一个函数接受任意数量的位置参数,可以使用一个* 参数 在这个例子中,rest 是由所有其他位置参数组成的元组.然后我们在代码中把它当成了一个序列来进行后续的计算
- Linux是一个基于POSIX和Unix的多用户、多任务、支持多线程和多CPU的性能稳定的操作系统,可免费使用并自由传播。
Linux是一个基于POSIX和Unix的多用户.多任务.支持多线程和多CPU的性能稳定的操作系统,可免费使用并自由传播. Linux是众多操作系统之一 , 目前流行的服务器和 PC 端操作系统有 L ...
- Memcahce(MC)系列(两)Linux下一个Memcache安装
Linux下一个memcache安装 memcache是高性能.分布式的内存对象缓存系统,用于在动态应用中降低数据库负载.提升訪问速度.眼下用memcache解决互联网上的大用户读取是很流行的一种使用 ...
- linux—粘滞位的设置
粘滞位(Stickybit),或粘着位,是Unix文件系统权限的一个旗标.最常见的用法在目录上设置粘滞位,如此以来,只有目录内文件的所有者或者root才可以删除或移动该文件.如果不为目录设置粘滞位,任 ...
- Linux粘滞位的设置
粘滞位(Stickybit),或粘着位,是Unix文件系统权限的一个旗标.最常见的用法在目录上设置粘滞位,如此以来,只有目录内文件的所有者或者root才可以删除或移动该文件.如果不为目录设置粘滞位,任 ...
- 5-3 Linux内核计时、延时函数与内核定时器【转】
转自:http://www.xuebuyuan.com/510594.html 5-3 Linux内核计时.延时函数与内核定时器 计时 1. 内核时钟 1.1 内核通过定时器(timer)中断来跟 ...
- CSS之background-image:在一个元素中设置给定数量的背景图片
众所周知,可以通过设置background-repeat的值来改变背景图片的重复次数.但有一个问题,background-repeat的值不是让图片只有1个,就是让图片铺满.如果只想设置给定数量的图片 ...
随机推荐
- 关于vs2013进行单元测试
安装vs的过程就不多说了,做为一个学计算机的学生十基本技能. 第一步建立新工程.使用c#语言, 第二步,建立一个类.输入要测试的代码 第三步 建立一个类 第四步 运行测试
- [Cyan之旅]使用NPOI实现Excel的导入导出,踩坑若干.
Cyan是博主[Soar360]自2014年以来开始编写整理的工具组件,用于解决现实工作中常用且与业务逻辑无关的问题. 什么是NPOI? NPOI 是 POI 项目的 .NET 版本.POI是一个开源 ...
- ElasticSearch 2 (30) - 信息聚合系列之条形图
ElasticSearch 2 (30) - 信息聚合系列之条形图 摘要 版本 elasticsearch版本: elasticsearch-2.x 内容 聚合还有一个令人激动的特性就是能够十分容易地 ...
- ElasticSearch 2 (26) - 语言处理系列之打字或拼写错误
ElasticSearch 2 (26) - 语言处理系列之打字或拼写错误 摘要 我们喜欢在对结构化数据(如:日期和价格)做查询时,结果只返回那些能精确匹配的文档.但是,好的全文搜索不应该有这样的限制 ...
- JS基础(二)数据类型
一.标量类型 1.字符串string类型:字符串需要用定界符包裹.定界符:单引号(‘’),双引号(“”). 2.数字类型:1)整型:所有整数 2)浮点型:所有浮点数 3.boolean类型:返回tru ...
- 在 SQL Server 中从完整路径提取文件名(sql 玩转文件路径)
四个函数: --1.根据路径获取文件名 -- ============================================= -- Author: Paul Griffin -- Crea ...
- [知乎]关于WindowsXPx64SP2系统的说明
自己简单安装了下发现 winxpsp3x86的系统版本为: 然后windowsXPx64sp2的版本为: 作者:qpi667链接:https://www.zhihu.com/question/29 ...
- 本地安装apk后直接打开,按下Home键再重新打开,然后按下返回键时页面展示错误的处理方法
情景: 1.下载apk到手机本地,点击本地apk开始安装 2.安装完成后,一般会有 “完成” 和 “打开” 两个按钮,点击 “完成” 按钮时是没有问题的,不管它 3.点击 “打开” 按钮,进入到首页( ...
- 【版本管理】自定义git
Git除了可配置user.name和user.email外,实际上,Git还有很多可配置项. 如 $ git config --global color.ui true,让Git显⽰示颜⾊色,会让命令 ...
- 深入理解ajax系列第八篇——表单提交
前面的话 在以前,网站的用户与后端交互的主要方式是通过HTML表单的使用.表单的引入在1993年,由于其简单性和易用性,直到电子商务出现之前一直保持着重要位置.理解表单提交,对于更深入地理解ajax是 ...