linux定时器

1、 内核定时器分类

80x86体系结构上,内核与时钟密切相关,与时钟相关的硬件有:实时时钟(Real Time Clock,RTC)、时间戳计数器(Time Stamp Counter,TSC)、可编程间隔定时器(Programmable Interval Timer, PIT)†SMP系统上的本地APIC定时器高精度事件定时器(High Precision Event Timer,HPET)。

RTC是一个独立于CPU的专用芯片,它依靠独立于系统供电电源的小电池给RTC的振荡器进行供电,因此在关机时也能保证时间是正确的。即使关闭PC电源,也会继续运转。这些芯片一般与主板的CMOS芯片组集成在一个芯片中。
†例如:Motorala 146818,实时时钟中断是从IRQ8上引入的,能发出周期性的中断,频率在2HZ~8192之间,可以对其编程实现对时间的长短计时。

tick(滴答)是HZ的倒数,也就是发生两次定时中断的时间间隔。如HZ为100时,tick为1/100=10ms(毫秒)。†jiffies为Linux内核中的一个全局变量,用来记录从系统启动以来产生的节拍数,extern u64 __jiffy_data jiffies_64;extern unsigned long volatile __jiffy_data jiffies;

节拍率,即系统定时器的频率,在内核中通过HZ这个宏进行定义。在进行内核编程的时候,切记不要假设HZ不会发生变化,事实上,大多数体系结构的HZ都是可调的

HZ的理想取值:从2.5内核开始,这个取值在i386体系结构中就改为了1000(2.6.13版本后的内核,加入了250这个取值)。改变HZ的取值,对于操作系统而言,意味着改变时钟中断的频率:

墙上时间:墙上挂钟的时间,即真实时间。用户级进程一般使用该时间。

进程时间:进程消耗的时间,包括用户空间的和内核空间的,一般在对程序进行分析、统计的时候使用。

单调时间:保持稳定线性递增的时间,其重要性不在于绝对值是多少,而在于通过多次采样并计算相对时间。单调时间不会因为墙上时间的改变而受到影响。

1.1.1      基本数据类型

typedef long  time_t

这个记录的是自UTC以来,流逝过的秒数。

一个问题是,是否会溢出?当然会,对于32位机,在2038年会溢出,但幸运的是,那个时候,很可能我们已经在使用64位机了

1.1.1      毫秒级与纳秒级

struct timeval{

       time_t  tv_sec;    // seconds

       susecond_t  tv_usec;              // microseconds

}

struct timespec{

       time_t  tv_sec;    // seconds

       long  tv_nsec;            // nanoseconds

}

显而易见的原因,后者更受用户的青睐。

1.1.1      标准c提供的时间

struct tm{

       ···

}      // 定义在<time.h>中,使用我们更容易理解的方式,更生活化

1.1.2      进程时间类型

typedef long clock_t;  对于不同函数,该变量表示HZ,或者CLOCKS_PER_SEC

1.1        接口

系统提供的接口主要有两类:获取、设置时间。前者还包括进程时间的获取

另外,也能够对系统时钟进行调校

接口简单罗列如下:

time()                   // s                ßà stime()

gettimeofday()     // us              ßà settimeofday()

clock_gettime()    // ns              ßàclock_settime()

times()                 // clock_t

另外,系统还提供各种数据结构转换的接口,调校时钟的接口等

1        Linux定时器

1.1        Linux提供的睡眠系统调用– 临时定时器

Linux提供了几个睡眠的系统调用

sleep()                        // s

usleep()                      // us

nanosleep()                // ns

clock_nanosleep()    //POSIX规范接口,更高级,提供各种类型的定时器

1.1        进程唯一的实时定时器

对于进程唯一的实时定时器的使用,系统提供了两类接口:alarm / ualarm,以及 getitimer /setitimer

前者是简单的小闹钟,结合信号量使用,精确度在秒级

后者则更为高级,其提供了3种工作模式,能够较好的过滤掉诸如上下文切换带来的统计误差:

REAL:记录墙上时间的流逝(SIGALARM)

VIRTUAL:只记录进程空间的时间流逝(SIGVTALARM)

PROF:在进程执行以及内核为进程服务时,记录时间的流逝(SIGPROF)

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

使用定时器的目的无非是为了周期性的执行某一任务,或者是到了一个指定时间去执行某一个任务。要达到这一目的,一般有两个常见的比较有效的方法。一个是用linux内部的三个定时器,另一个是用sleep, usleep函数让进程睡眠一段时间,使用alarm定时发出一个信号,还有那就是用gettimeofday, difftime等自己来计算时间间隔,然后时间到了就执行某一任务,但是这种方法效率低,所以不常用。

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void sigalrm_fn(int sig)
{
        /* Do something */
        printf("alarm!\n");
        alarm(2);
        return;
}
int main(void)
{
        signal(SIGALRM, sigalrm_fn);
        alarm(2);
        /* Do someting */
        while(1) pause();
}

//********************************************************************************//

2 Signals:
Signal
Description
SIGABRT
由调用abort函数产生,进程非正常退出
SIGALRM
用alarm函数设置的timer超时或setitimer函数设置的interval timer超时
SIGBUS
某种特定的硬件异常,通常由内存访问引起
SIGCANCEL
由Solaris Thread Library内部使用,通常不会使用
SIGCHLD
进程Terminate或Stop的时候,SIGCHLD会发送给它的父进程。缺省情况下该Signal会被忽略
SIGCONT
当被stop的进程恢复运行的时候,自动发送
SIGEMT
和实现相关的硬件异常
SIGFPE
数学相关的异常,如被0除,浮点溢出,等等
SIGFREEZE
Solaris专用,Hiberate或者Suspended时候发送
SIGHUP
发送给具有Terminal的Controlling Process,当terminal被disconnect时候发送
SIGILL
非法指令异常
SIGINFO
BSD signal。由Status Key产生,通常是CTRL+T。发送给所有Foreground Group的进程
SIGINT
由Interrupt Key产生,通常是CTRL+C或者DELETE。发送给所有ForeGround Group的进程
SIGIO
异步IO事件
SIGIOT
实现相关的硬件异常,一般对应SIGABRT
SIGKILL
无法处理和忽略。中止某个进程
SIGLWP
由Solaris Thread Libray内部使用
SIGPIPE
在reader中止之后写Pipe的时候发送
SIGPOLL
当某个事件发送给Pollable Device的时候发送
SIGPROF
Setitimer指定的Profiling Interval Timer所产生
SIGPWR
和系统相关。和UPS相关。
SIGQUIT
输入Quit Key的时候(CTRL+\)发送给所有Foreground Group的进程
SIGSEGV
非法内存访问
SIGSTKFLT
Linux专用,数学协处理器的栈异常
SIGSTOP
中止进程。无法处理和忽略。
SIGSYS
非法系统调用
SIGTERM
请求中止进程,kill命令缺省发送
SIGTHAW
Solaris专用,从Suspend恢复时候发送
SIGTRAP
实现相关的硬件异常。一般是调试异常
SIGTSTP
Suspend Key,一般是Ctrl+Z。发送给所有Foreground Group的进程
SIGTTIN
当Background Group的进程尝试读取Terminal的时候发送
SIGTTOU
当Background Group的进程尝试写Terminal的时候发送
SIGURG
当out-of-band data接收的时候可能发送
SIGUSR1
用户自定义signal 1
SIGUSR2
用户自定义signal 2
SIGVTALRM
setitimer函数设置的Virtual Interval Timer超时的时候
SIGWAITING
Solaris Thread Library内部实现专用
SIGWINCH
当Terminal的窗口大小改变的时候,发送给Foreground Group的所有进程
SIGXCPU
当CPU时间限制超时的时候
SIGXFSZ
进程超过文件大小限制
SIGXRES
Solaris专用,进程超过资源限制的时候发送

所需头文件

#include<unistd.h>
unsigned int alarm(unsigned int seconds);
成功:如果调用此alarm()前,进程已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0。
出错:-1
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
Linux内置的3个定时器

Linux为每个任务安排了3个内部定时器:

ITIMER_REAL:实时定时器,不管进程在何种模式下运行(甚至在进程被挂起时),它总在计数。定时到达,向进程发送SIGALRM信号。

ITIMER_VIRTUAL:这个不是实时定时器,当进程在用户模式(即程序执行时)计算进程执行的时间。定时到达后向该进程发送SIGVTALRM信号。 

ITIMER_PROF:进程在用户模式(即程序执行时)和核心模式(即进程调度用时)均计数。定时到达产生SIGPROF信号。ITIMER_PROF记录的时间比ITIMER_VIRTUAL多了进程调度所花的时间。

定时器在初始化是,被赋予一个初始值,随时间递减,递减至0后发出信号,同时恢复初始值。在任务中,我们可以一种或者全部三种定时器,但同一时刻同一类型的定时器只能使用一个。

使用滴答数计时。

用到的函数有:

#include <sys/time.h>
int getitimer(int which, struct itimerval *value);
int setitimer(int which, struct itimerval*newvalue, struct itimerval* oldvalue);
strcut timeval
{
long tv_sec; /*秒*/
long tv_usec; /*微秒*/
};
struct itimerval
{
struct timeval it_interval; /*时间间隔*/
struct timeval it_value;   /*当前时间计数*/
};

it_interval用来指定每隔多长时间执行任务, it_value用来保存当前时间离执行任务还有多长时间。比如说, 你指定it_interval为2秒(微秒为0),开始的时候我们把it_value的时间也设定为2秒(微秒为0),当过了一秒, it_value就减少一个为1, 再过1秒,则it_value又减少1,变为0,这个时候发出信号(告诉用户时间到了,可以执行任务了),并且系统自动把it_value的时间重置为it_interval的值,即2秒,再重新计数。

用select()函数可以实现定时,而且可以将时间精确到毫秒级:
 
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
#include <stdlib.h>
#include <signal.h>
int count = 0;
void set_timer()
{
        struct itimerval itv, oldtv;
        itv.it_interval.tv_sec = 5;
        itv.it_interval.tv_usec = 0;
        itv.it_value.tv_sec = 5;
        itv.it_value.tv_usec = 0;

        setitimer(ITIMER_REAL, &itv, &oldtv);
}

void sigalrm_handler(int sig)
{
        count++;
        printf("timer signal.. %d\n", count);
}

int main()
{
        signal(SIGALRM, sigalrm_handler);
        set_timer();
        while (count < 1000)
        {}
        exit(0);
}

which为定时器类型,setitimer支持3种类型的定时器:
ITIMER_REAL: 以系统真实的时间来计算,它送出SIGALRM信号。
ITIMER_VIRTUAL: -以该进程在用户态下花费的时间来计算,它送出SIGVTALRM信号。
ITIMER_PROF: 以该进程在用户态下和内核态下所费的时间来计算,它送出SIGPROF信号。
setitimer()第一个参数which指定定时器类型(上面三种之一);第二个参数是结构itimerval的一个实例;第三个参数可不做处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值