Linux使用一個定時器實(shí)現(xiàn)設(shè)置任意數(shù)量定時器功能
為什么需要這個功能,因?yàn)榇蠖鄶?shù)計算機(jī)軟件時鐘系統(tǒng)通常只能有一個時鐘觸發(fā)一次中斷。當(dāng)運(yùn)行多個任務(wù)時,我們會想要多個定時器 的時鐘跟蹤并發(fā)這樣可以生成正確的時間重疊,操作系統(tǒng)這樣做。
本例子是為了實(shí)現(xiàn)使用Linux下的一個定時器,實(shí)現(xiàn)任一數(shù)量的定時器功能。
首先我們需要一些數(shù)據(jù)類型用來描述時鐘數(shù)據(jù)結(jié)構(gòu)
#include <stdio.h>
#include<time.h>
#define TRUE 1
#define FALSE 0
#define MAX_TIMERS ... 最大時鐘數(shù)量
typedef timerval TIME; 定義時間類型
#define VERY_LONG_TIME ... 最大時間長度
struct timer {
int inuse; 時鐘是否可用
TIME time; 定時時間長度
char *event; 是否超時
} timers[MAX_TIMERS]; /* set of timers */
每個定時器都以這個數(shù)據(jù)結(jié)構(gòu)來描述,第一個成員用來描述時鐘是否正在使用,第二個成員是這個定時器的定時時間,第三個成員是是一個指針,*event初始化應(yīng)該為0,當(dāng)他被置為1,我們知道這個定時器已經(jīng)超時了,和他相關(guān)的任務(wù)可以執(zhí)行。
接下來是定時器數(shù)組的初始化,這里將每個時鐘inuse成員設(shè)置為FALSE,表示時鐘不可用。
void
timers_init() {
struct timer *t;
for (t=timers;t<&timers[MAX_TIMERS];t++)
t->inuse = FALSE;
}
現(xiàn)在開始是結(jié)構(gòu)實(shí)現(xiàn)部分
首先寫到的timer_undeclare這個函數(shù),這個函數(shù)與后面的timer_declare相對立。主要作用是清除一個定時器。
有很多方法可以用來保存定時器的定時記錄。沒有復(fù)雜時鐘硬件的機(jī)器通常在每一個時鐘周期處理一個中斷處理程序。然后軟件就在處理程序中獲取系統(tǒng)時間,然后判斷是否設(shè)置的定時器超時。
很多比較聰明的機(jī)器可以在硬件中設(shè)置定時時間,一旦時間超時,就觸發(fā)一個硬件中斷。這同樣適用與軟件中斷。
他們通過一個 定義一個time_now來記錄當(dāng)前的系統(tǒng)時間,volatile告訴機(jī)器每次從寄存器取值,防止數(shù)據(jù)被系統(tǒng)優(yōu)化。
volatile TIME time_now
接下來定義一系列數(shù)據(jù)來記錄 timer_next 指接下來要我們想要計時的定時器。time_timer_set保存最后一次獲取的系統(tǒng)時間。
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作用為取消一個定時器。首先讓中斷失效,這很重要,因?yàn)闀r鐘數(shù)據(jù)結(jié)構(gòu)數(shù)據(jù)是在各進(jìn)程中共享的,是可以在其他中斷中被修改的,為了防止不必要的錯我,這個取消操作應(yīng)該為一個原子操作。開始我們先檢測是否這個時鐘已經(jīng)無效了。如果有效,則設(shè)置inuse使其失效。如果我們要取消的定時器正好是下一個期望等待的定時器。那我們要重新指定下一個期望等待的定時器。在此之前所有定時器都要更新一下前一個定時器已經(jīng)走過的時間。
接下來我們看到timers_update(time_t ti)函數(shù)
//更新定時器表時間
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;
}
此函數(shù)作用是更新所有有效定時器的時間長,同時將timer_next指向當(dāng)前延時時間最短的一個定時器。如沒有定時器,則將timer_next設(shè)置為空。
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;
}
首先找到一個可用的定時器表項(xiàng),設(shè)置相關(guān)參數(shù)。
接下來判斷如果timer_next為空,那么說明定時器表項(xiàng)沒有定時器需要定時,那我們直接將timer_next指向新加入定時器,開始計時。
如果新加入定時器需要延時時間比當(dāng)前正在延時的定時器的剩余時間還要短,則更新定時器表,并計時當(dāng)前加入的定時器。
在處理完當(dāng)前定時器事件后,將新加入的定時器的inuse置1.
接下來是定時器中斷處理函數(shù)
//定時器中斷處理函數(shù)
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);
}
}
這里我們打印一串字符來證明定時器時間的觸發(fā),首先要做的先更新定時器表,然后將time_timer_set設(shè)置成當(dāng)前系統(tǒng)時間,繼續(xù)進(jìn)行下一個定時器事件,直到所有定時器都處理完畢。
接下來幾個是LINUX系統(tǒng)相關(guān)函數(shù)
//失效定時器中斷
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);
}
主函數(shù)測試部分
#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);
}
實(shí)驗(yàn)結(jié)果:
parent child 2 child 1 interrupt_hander interrupt_hander interrupt_hander
總結(jié)
以上所述是小編給大家介紹的Linux使用一個定時器實(shí)現(xiàn)設(shè)置任意數(shù)量定時器功能,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
Linux CentOS MySQL 5.7.18 5.7.X安裝教程
這篇文章主要介紹了Linux CentOS MySQL 5.7.18 5.7.X安裝教程,需要的朋友可以參考下2017-04-04
解決Cent0S 6.7直接在/etc/resolv.conf文件下修改DNS地址重啟不生效問題
這篇文章主要介紹了解決Cent0S 6.7直接在/etc/resolv.conf文件下修改DNS地址重啟不生效問題 ,需要的朋友可以參考下2017-07-07
Centos系統(tǒng)中徹底刪除Mysql數(shù)據(jù)庫步驟
這篇文章主要介紹了Centos系統(tǒng)中徹底刪除Mysql數(shù)據(jù)庫步驟的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-07-07
Linux應(yīng)用調(diào)試使用gdb和gdbserver命令詳解
這篇文章主要介紹了Linux應(yīng)用調(diào)試使用gdb和gdbserver的相關(guān)資料,需要的朋友可以參考下2017-12-12

