Linux下的高精度时间获得与定时器
本文主要介绍Linux下高精度时间函数,及相关的具有超时机制的函数,对定时器也进行简单的介绍。
在linux下通常可用的精度最高的时间接口是gettimeofday,它返回一个timeval结构,其精度为us,即10-6 秒,大多数情况这个精度已经够用了。不过有时为了更高的精度,比如纳秒级的时间精度,我们需求探索Linux为我们提供的时间调用。
首先介绍struct timespec结构,这个结构体有两个成员,一个是秒,一个是纳秒。
在librt库中,提供了高精度的时间函数,分别是:
long clock_gettime(clockid_t ,struct timespec*)
获取特定时钟的时间,时间通过fp结构传回,目前定义了6种时钟,分别是
CLOCK_REALTIME 系统当前时间,从1970年1.1日算起 CLOCK_MONOTONIC 系统的启动时间,不能被设置 CLOCK_PROCESS_CPUTIME_ID 进程运行时间 CLOCK_THREAD_CPUTIME_ID 线程运行时间 CLOCK_REALTIME_HR CLOCK_REALTIME的高精度版本 CLOCK_MONOTONIC_HR CLOCK_MONOTONIC的高精度版本
获取特定时钟的时间精度:
long clock_getres(clockid_t )
设置特定时钟的时间:
long clock_settime(clockid_t ,struct timespec*)
休眠time中指定的时间,如果遇到信号中断而提前返回,则由left_time返回剩余的时间:
long clock_nanosleep(clockid_t ,int flag,timespec* time,timespec* left_time)
有了这些个时间函数之后,我们再来看下如何实现一些不同精度的简单的定时器。
最粗糙的定时器可以由sleep来实现,其精度为秒级,系统也提供像nanosleep,usleep,ualarm等,当然你愿意也可以由poll(ms)、select(us)、ppoll或pslect(ns)等来实现各种精度的sleep。通过这些高精度的sleep函数,也可以实现一系统不同精度的定时器。
通过上述sleep实现的定时器通常需要我们自行进行编码,而且过多的sleep也会导致某个cpu不能充分的利用,对于大量定时器的场合就需要小心编写代码,这种方式通常以单独线程控制或主循环轮询的方式查看哪些定时器到期。总体来说,实现复杂,效率较低,而且也没有一种好的定时器到期时的通知机制,通常是被动由定时器线程强行执行或者自身线程在线程主循环中检查到期的定时器并执行。
下面我们将探索一下由操作系统提供的一些定时器机制。操作系统提供了两个种类的定时器,一种是显式的定时器,另一种是隐藏在调用的超时时间或特定文件属性之上。后者我们在前面已经见到过,比如select、套接字描述符的超时属性,这些需要在不同的编程领域去积累,当然它们也有各自的精度。下面我们主要介绍一下系统提供的显式的定时器。
Linux系统为每个进程提供了三个间隔定时器,精度为us。定时器到期时将触发相应的信号,定时器可能会重新开始,值得注意的是,fork生成的子进程并不继承父进程的定时器。
int getitimer(int type, itimerval* val) int setitimer(int type, itimerval* nval, itimerval* oval)
itimerval 有两个成员,it_interval和it_value,均为timeval类型,前者保存的是下次定时器的定时间隔,后者为本次超时时的定时间隔。也就是说,定时器将从it_value减少,当变为0时,发送信号,并从it_interval取值,重新开始定时器。如果val被置为0,则停止定时器。
getitimer()用于设置一个定时器,而setitimer用于修改已经设置的定时器,如果在设置时定时器尚未超时,则剩余时间保存于oval。
定时器定时器三个定时器分别是:
ITIMER_REAL 以系统时间递减,超时时投递SIGALRM信号 ITIMER_VIRTUAL 以进程执行时间递减,超时时投递 SIGVTALRM ITIMER_PROF 当进程执行或进程执行系统调用时均递减,超时时投递SIGPROF信号。
此外Posix1.b中为我们提供了实时高精度的定时工具,精度可以达到纳秒。不过每个进程只能有一个。