posix555定时器器 555定时器中往下进行吗

博客访问: 1233208
博文数量: 376
博客积分: 10828
博客等级: 上将
技术积分: 4167
注册时间:
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: LINUX
Linux下的定时器有两种,以下分别介绍:
如果不要求很精确的话,用alarm()和signal()就够了
unsigned int alarm(unsigned int seconds)
函数说明: alarm()用来设置信号SIGALRM在经过参数seconds指定的秒数后传送给目前的进程。如果参数seconds为0,则之前设置的闹钟会被取消,并将剩下的时间返回。
返回值: 返回之前闹钟的剩余秒数,如果之前未设闹钟则返回0。
alarm()执行后,进程将继续执行,在后期(alarm以后)的执行过程中将会在seconds秒后收到信号SIGALRM并执行其处理函数。
#include #include #include void sigalrm_fn(int sig){printf("alarm!\n");alarm(2);}int main(void){signal(SIGALRM, sigalrm_fn);alarm(1);while(1) pause();}
2、setitimer()
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue));
setitimer()比alarm功能强大,支持3种类型的定时器:
ITIMER_REAL : 以系统真实的时间来计算,它送出SIGALRM信号。
ITIMER_VIRTUAL : -以该进程在用户态下花费的时间来计算,它送出SIGVTALRM信号。
ITIMER_PROF : 以该进程在用户态下和内核态下所费的时间来计算,它送出SIGPROF信号。
setitimer()第一个参数which指定定时器类型(上面三种之一);第二个参数是结构itimerval的一个实例;第三个参数可不做处理。
setitimer()调用成功返回0,否则返回-1。
下面是关于setitimer调用的一个简单示范,在该例子中,每隔一秒发出一个SIGALRM,每隔0.5秒发出一个SIGVTALRM信号:
#include #include #include #include #include #include void sigroutine(int signo){switch (signo){case SIGALRM:printf("Catch a signal -- SIGALRM \n");signal(SIGALRM, sigroutine);case SIGVTALRM:printf("Catch a signal -- SIGVTALRM \n");signal(SIGVTALRM, sigroutine);}}int main(){struct itimerval value, ovalue, value2; //(1)sec = 5;printf("process id is %d\n", getpid());signal(SIGALRM, sigroutine);signal(SIGVTALRM, sigroutine);value.it_value.tv_sec = 1;value.it_value.tv_usec = 0;value.it_interval.tv_sec = 1;value.it_interval.tv_usec = 0;setitimer(ITIMER_REAL, &value, &ovalue); //(2)value2.it_value.tv_sec = 0;value2.it_value.tv_usec = 500000;value2.it_interval.tv_sec = 0;value2.it_interval.tv_usec = 500000;setitimer(ITIMER_VIRTUAL, &value2, &ovalue);for(;;);}
(1) struct itimerval
struct itimerval {struct timeval it_ /* timer interval */struct timeval it_ /* current value */};itimerval: i --> intervalval --> value
itimerval结构中的it_value是减少的时间,当这个值为0的时候就发出相应的信号了. 然后再将it_value设置为it_interval值.
(2) setitimer()
setitimer()为其所在进程设置一个定时器,如果itimerval.it_interval不为0(it_interval的两个域都不为0),则该定时器将持续有效(每隔一段时间就会发送一个信号)
注意:Linux信号机制基本上是从Unix系统中继承过来的。早期Unix系统中
的信号机制比较简单和原始,后来在实践中暴露出一些问题,因此,把那些建立在早期机制上的信号叫做"不可靠信号",信号值小于
SIGRTMIN(SIGRTMIN=32,SIGRTMAX=63)的信号都是不可靠信号。这就是"不可靠信号"的来源。它的主要问题是:进程每次处理
信号后,就将对信号的响应设置为默认动作。在某些情况下,将导致对信号的错误处理;因此,用户如果不希望这样的操作,那么就要在信号处理函数结尾再一次调
用signal(),重新安装该信号。 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------setitimer()为Linux的API,并非C语言的Standard
Library,setitimer()有两个功能,一是指定一段时间后,才执行某个function,二是每间格一段时间就执行某个function,
以下程序demo如何使用setitimer()。
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&#include&&&&&//&for&printf()&&&#include&&&&//&for&pause()&&&&#include&&&&//&for&signal()&&&#include&&&&//&for&memset()&&&&#include&&//&struct&itimeral.&setitimer()&&&&&&&&void&printMsg(int);&&&&&&&&int&main()&{&&&&&&&&&&&&int&res&=&0;&&&&&&&&&&&&signal(SIGALRM,&printMsg);&&&&&&&&&&&&struct&itimerval&&&&&&&&&&&&&memset(&tick,&0,&sizeof(tick));&&&&&&&&&&&&tick.it_value.tv_sec&=&1;&&&&&&&&tick.it_value.tv_usec&=&0;&&&&&&&&&&&&&tick.it_interval.tv_sec&=&1;&&&&&&tick.it_interval.tv_usec&=&0;&&&&&&&&&&&&&&&&&&res&=&setitimer(ITIMER_REAL,&&tick,&NULL);&&&&&&if&(res)&{&&&&&&&&printf("Set&timer&failed!!\n");&&&&&&}&&&&&&&&&&&&&&&&while(1)&{&&&&&&&&pause();&&&&&&}&&&&&&&&&&return&0;&&&&&&}&&&&&&&&void&printMsg(int&num)&{&&&&&&printf("%s","Hello&World!!\n");&&&&}&&/*
: timer.cpp
: gcc 4.1.0 on Fedora Core 5
Description : setitimer() set the interval to run function
: #include
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue);
struct itimerval {
struct timerval it_
struct timerval it_
struct timeval {
: 11/25/2006
// for printf()
// for pause()
// for signal()
// for memset()
// struct itimeral. setitimer()
void printMsg(int);
int main() {
// Get system call result to determine successful or failed
int res = 0;
// Register printMsg to SIGALRM
signal(SIGALRM, printMsg);
// Initialize struct
memset(&tick, 0, sizeof(tick));
// Timeout to run function first time
tick.it_value.tv_sec = 1;
tick.it_value.tv_usec = 0; // micro sec.
// Interval time to run function
tick.it_interval.tv_sec = 1;
tick.it_interval.tv_usec = 0;
// Set timer, ITIMER_REAL : real-time to decrease timer,
send SIGALRM when timeout
res = setitimer(ITIMER_REAL, &tick, NULL);
if (res) {
printf("Set timer failed!!\n");
// Always sleep to catch SIGALRM signal
while(1) {
void printMsg(int num) {
printf("%s","Hello World!!\n");
当setitimer()所执行的timer时间到了,会呼叫SIGALRM
signal,所以在第30行用signal()将要执行的function指定给SIGALRM。
在第43行呼叫setitimer()设定timer,但setitimer()第二个参数是sturct,负责设定timeout时间,所以第36行到
第 40行设定此struct。itimerval.it_value设定第一次执行function所延迟的秒数,
itimerval.it_interval设定以后每几秒执行function,所以若只想延迟一段时间执行function,只要设定
itimerval.it_value即可,若要设定间格一段时间就执行function,则it_value和it_interval都要设定,否则
funtion的第一次无法执行,就别说以后的间隔执行了。 第36行和第39行的tv_sec为sec,第37行和40行为micro
sec(0.001 sec)。
第43行的第一个参数ITIMER_REAL,表示以real-time方式减少timer,在timeout时会送出SIGALRM
signal。第三个参数会存放旧的timeout值,如果不需要的话,指定NULL即可。 第47
行的pause(),命令系统进入sleep状态,等待任何signal,一定要用while(1)无穷循环执行pause(),如此才能一直接收
SIGALRM signal以间隔执行function,若拿掉while(1),则function只会执行一次而已。---------------------------------------------------------------------------------------------------------------------------------------------------------& 下
还有一种高精度的定时器,那就是posix_timer.我记得以前看代码的时候CLOCK_REALTIME的定时器似乎用的就是rdtsc指令,不过
现在不确定了,先放到一边。原理上来说,可以在变频的时候也使用rdtsc指令,因为CPU的频率我们也是知道的,变频的时候内核也是知道的。
&&& 下面是我的timer_create的例子,编译的时候要加上rt库,这是的realtime库:
&&& gcc -o test test.c
&#include #include #include #include #include
#define rdtsc(low,high) __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
void handler (int sig, siginfo_t * extra, void *cruft){& static last_i=0;& unsigned int i,& rdtsc(i,j);& printf ("time:%u, %u, [%u] %uHZ ", j, i, i-last_i, (i-last_i)*10/1000000);& last_i =}
int main (){& int i=0;& sigset_
& sigfillset (&sigset);& sigdelset (&sigset, SIGRTMIN);& sigprocmask (SIG_SETMASK, &sigset, NULL);
&& sigfillset (&sa.sa_mask);& sa.sa_flags = SA_SIGINFO;& sa.sa_sigaction =
& if (sigaction (SIGRTMIN, &sa, NULL) < 0)& {&&& perror ("sigaction failed ");&&& exit (-1);& }
& struct sigevent timer_& str
& timer.it_interval.tv_sec = 0;& timer.it_interval.tv_nsec = 100 * 1000 * 1000;& timer.it_value = timer.it_
& timer_event.sigev_notify = SIGEV_SIGNAL;& timer_event.sigev_signo = SIGRTMIN;& timer_event.sigev_value.sival_ptr = (void *) &
& if (timer_create (CLOCK_REALTIME, &timer_event, &tt) < 0)& {&&& perror ("timer_create failed");&&& exit (-1);& }
& if (timer_settime (tt, 0, &timer, NULL) < 0)& {&&& perror ("timer_settime failed");&&& exit (-1);& }
& while (i++ < 10)& {&&& pause ();& }
& return 0;}
&&& 输出结果:
time:, [] 2163HZtime:, [] 1861HZtime:, [] 1861HZtime:, [] 1880HZtime:, [] 1861HZtime:, [] 1861HZtime:, [] 1861HZtime:, [] 1861HZtime:, [] 1861HZtime:, [] 1861HZ
阅读(2414) | 评论(1) | 转发(0) |
相关热门文章
给主人留下些什么吧!~~
定时器的demo写的很详细,好文章!!
请登录后评论。Linux 下定时器的实现方式分析_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
Linux 下定时器的实现方式分析
上传于||文档简介
&&L&#8203;i&#8203;n&#8203;u&#8203;x&#8203; &#8203;下&#8203;定&#8203;时&#8203;器&#8203;的&#8203;实&#8203;现&#8203;方&#8203;式&#8203;分&#8203;析
阅读已结束,如果下载本文需要使用0下载券
想免费下载更多文档?
下载文档到电脑,查找使用更方便
还剩13页未读,继续阅读
你可能喜欢Linux应用(47)
1、int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid)
&&&进程可以通过调用timer_create()创建特定的定时器,定时器是每个进程自己的,不是在fork时继承的。timer_create的参数clock_id说明定时器是基于哪个时钟的,*timerid装载的是被创建的定时器的ID。timer_create函数创建了定时器,并将他的ID放入timerid指向的位置中。参数evp指定了定时器到期要产生的异步通知。如果evp为NULL,那么定时器到期会产生默认的信号,对CLOCK_REALTIMER来说,默认信号就是SIGALRM。对那些定时器到期时要产生除默认信号之外的其它信号的定时器来说,程序必须将evp-&sigev_signo设置为期望的信号码。struct
sigevent&结构中的成员evp-&sigev_notify说明了定时器到期时应该采取的行动。通常,这个成员的&#20540;为SIGEV_SIGNAL,这个&#20540;说明在定时器到期时,会产生一个信号。程序可以将成员evp-&sigev_notify设为SIGEV_NONE来防止定时器到期时产生信号。
如果几个定时器产生了同一个信号,处理程序可以用evp-&sigev_value来区分是哪个定时器产生了信号。要实现这种功能,程序必须在为信号安装处理程序时,使用struct sigaction的成员sa_flags中的标志符SA_SIGINFO。
clock_id的取&#20540;为以下:
CLOCK_REALTIME :Systemwide realtime clock.
CLOCK_MONOTONIC:Represents monotonic time. Cannot be set.
CLOCK_PROCESS_CPUTIME_ID :High resolution per-process timer.
CLOCK_THREAD_CPUTIME_ID :Thread-specific timer.
CLOCK_REALTIME_HR :High resolution version of CLOCK_REALTIME.
CLOCK_MONOTONIC_HR :High resolution version of CLOCK_MONOTONIC.
struct sigevent
int sigev_ //notification type
int sigev_ //signal number
union sigval&& sigev_ //signal value
void (*sigev_notify_function)(union sigval);
pthread_attr_t *sigev_notify_
union sigval
int sival_ //integer value
void *sival_ //pointer value
通过将evp-&sigev_notify设定为如下&#20540;来定制定时器到期后的行为:
l&&&&&&&& SIGEV_SIGNAL:&发送由evp-&sigev_sino指定的信号到调用进程,evp-&sigev_value的&#20540;将被作为siginfo_t结构体中si_value的&#20540;。
l&&&&&&&& SIGEV_NONE:什么都不做,只提供通过timer_gettime和timer_getoverrun查询超时信息。
l&&&&&&&& SIGEV_THREAD:&以evp-&sigev_notification_attributes为线程属性创建一个线程,在新建的线程内部以evp-&sigev_value为参数调用evp-&sigev_notification_function。
2、int timer_delete(timer_t timerid);
删除ID为timerid的POSIX:TMR定时器。
3、int timer_settime(timer_t timerid,int flags,const struct itimerspec *value,struct itimerspec *ovalue);
struct&&&& itimerspec&&&
&&&&&& struct&&&& timespec&& it_ //定时器周期&#20540;
&&&&&& struct&&&& timespec&& it_&&&& //定时器到期&#20540;
&&&&& timer_settime负责启动或停止timer_create创建的定时器。参数flag说明定时器使用的是相对时间还是绝对时间。相对时间与POSIX:XSI定时器使用的策略类&#20284;,而绝对时间的精确度更高。参数vaule指向的&#20540;来设置timerid指定的定时器。如果ovalue不为NULL,timer_settime就将定时器以前的&#20540;放在ovalue指定的位置上。如果定时器正在运行,那么*ovalue的成员it_value非零,并包含了定时器到期之前剩余的时间。
TIMER_ABSTIME表示绝对时间;
如果flag没有设定为TIMER_ABSTIME,则定时器从调用开始在it_value内超时;即value-&it_value代表计时器第一次超时的时间。
如果设定为TIMER_ABSTIME,该函数表现为时间直到下一次超时被设定为it_value指定的绝对时间和与timerid相联的时钟&#20540;的差&#20540;。如果已经到了it_value指定的&#20540;,那么超时后的处理就会立即执行。
定时器的再装由value的it_interval成员&#20540;来设定。
4、int timer_gettime(timer_t timerid,struct itimerspec *value);
获得一个活动定时器的剩余时间。
int timer_getoverrun(timer_t timerid);
&&&&&&&有可能一个定时器到期了,而同一定时器上一次到期时产生的信号还处于挂起状态。在这种情况下,其中的一个信号可能会丢失。这就是定时器超限。程序可以通过调用timer_getoverrun来确定一个特定的定时器出现这种超限的次数。定时器超限只能发生在同一个定时器产生的信号上。由多个定时器,甚至是那些使用相同的时钟和信号的定时器,所产生的信号都会排队而不会丢失。
5、api接口定义
timer_t g_timerid = 0;
/****************************************************************************
&功 & &能: & 定时器回调函数
&输入参数: v:sigev_value作为入参
&输出参数:
&返 回 &#20540;: & 无
****************************************************************************/
static void timer_thread(union sigval v) &
debug(DBG_DEBUG, &timer_thread\n&);
/****************************************************************************
&功 & &能: & 定时器创建
& 定时时间到调用回调函数timer_thread
&输入参数: 无
&输出参数:无
&返 回 &#20540;: & 0:成功 -1:失败
****************************************************************************/
int InitTimer(void)
static int ITimerFlag = 0;
debug(DBG_DEBUG, &timer_create start\n&);
if (! ITimerFlag) {
& &memset(&evp, 0, sizeof(struct sigevent)); & & & //清零初始化 &
debug(DBG_DEBUG, &timer_create start\n&);
& &evp.sigev_value.sival_int = getpid(); & & & & & &//也是标识定时器的,这和timerid有什么区别?回调函数可以获得 &
& &evp.sigev_notify = SIGEV_THREAD; & & & & & &//线程通知的方式,派驻新线程 &
& &evp.sigev_notify_function = timer_ & & & //线程函数地址 &
& &if (timer_create(CLOCK_REALTIME, &evp, &g_timerid) == -1) &
debug(DBG_DEBUG, &timer_create Failed\n&);
& & & &return -1; &
ITimerFlag = 1;
/****************************************************************************
&功 & &能: & 定时器启动
&输入参数: sec:秒数
& usec:微妙
&输出参数:无
&返 回 &#20540;: & 0:成功 -1:失败
****************************************************************************/
int Api_BaseStartTimer(time_t sec, suseconds_t usec)
if (g_timerid == 0)
debug(DBG_DEBUG, &timerid Failed\n&);
return -1;
debug(DBG_DEBUG, &sec = %d, usec = %d\n&, sec, usec);
// 启动定时器,启动后立即执行一次,以后按照间隔来
& & value.it_interval.tv_sec &= &
& & value.it_interval.tv_nsec = &
& & value.it_value.tv_sec & & = 0; &
& & value.it_value.tv_nsec & &= 10;//切记要加上这个小时间,不然定时器无法生效。设置成0就不生效了!
if (timer_settime(g_timerid, 0, &value, NULL) == -1) &
& & debug(DBG_DEBUG, &fail to timer_settime\n&);
& & & & return -1;
& & return 0;
/****************************************************************************
&功 & &能: & 停止定时器
&输入参数: 无
&输出参数:无
&返 回 &#20540;: & 0:成功 -1:失败
****************************************************************************/
int StopTimer(void)
if (g_timerid == 0){
debug(DBG_DEBUG, &fail to timer_settime\n&);
return -1;
debug(DBG_DEBUG, &StopTimer \n&);
// 启动定时器 时间间隔设置成0
//未启动状态
& & value.it_interval.tv_sec &= 0; &
& & value.it_interval.tv_nsec = 0; &
& & value.it_value.tv_sec & & = 0; &
& & value.it_value.tv_nsec & &= 0;
if (timer_settime(g_timerid, 0, &value, NULL) == -1) &
& & debug(DBG_DEBUG, &fail to timer_settime\n&);
& & & & return -1; &
& & return 0;
/****************************************************************************
&功 & &能: & 删除定时器
&输入参数: 无
&输出参数:无
&返 回 &#20540;: & 0:成功 -1:失败
****************************************************************************/
int delettimer(void)
int iRet = -1;
iRet = timer_delete(g_timerid);
/****************************************************************************
&功 & &能: & 获取定时器剩余时间
&输入参数: 无
&输出参数:value
&返 回 &#20540;: & 0:成功 -1:失败
****************************************************************************/
int gettimer(itimerspec *value)
int iRet = -1;
iRet =&timer_gettime(g_timerid, value);
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:52508次
排名:千里之外
原创:28篇
转载:45篇
(2)(2)(3)(6)(7)(9)(10)(6)(3)(4)(1)(20)(3)博客访问: 1383989
博文数量: 377
博客积分: 10093
博客等级: 上将
技术积分: 4304
注册时间:
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: LINUX
简介这篇文章主要记录我在试图解决如何尽可能精确地在某个特定的时间间隔执行某项具体任务时的思路历程,并在后期对相关的API进行的归纳和总结,以备参考。问题引出很多时候,我们会有类似“每隔多长时间执行某项任务”的需求,乍看这个问题并不难解决,实则并不容易,有很多隐含条件需要考虑,诸如:时间精度是多少?时间是否允许出现偏差,允许的偏差是多少,偏差之后如何处理?系统的负载如何?这个程序允许占用的系统资源是否有限制?这个程序运行的硬件平台如何?为了便于分析,我们锁定题目为“每隔2妙打印当前的系统时间(距离UNIX纪元的秒数)”。基于sleep的朴素解法看到这个题目,我想大家的想法和我一样,都是首先想到类似这样的解法:
#include <stdio.h>
int main(int argc, char *argv[])
&&&&&&&&while (1) {
&&&&&&&&&&&&&&&&printf("%d\n", time(NULL));
&&&&&&&&&&&&&&&&sleep(2);
&&&&&&&&return 0;
}如果对时间精度要求不高,以上代码确实能工作的很好。因为sleep的时间精度只能到1s:&&&&&&
#include <unistd.h>
&&&&&&&unsigned int sleep(unsigned int seconds);所以对于更高的时间精度(比如说毫秒)来说,sleep就不能奏效了。如果沿着这个思路走下去,还分别有精确到微妙和纳秒的函数usleep和nanosleep可用:&&&&&
#include <unistd.h>
&&&&&&&int usleep(useconds_t usec);
&&&Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
&&&&&&&usleep(): _BSD_SOURCE || _XOPEN_SOURCE >= 500&&&&&
#include <time.h>
&&&&&&&int nanosleep(const struct timespec *req, struct timespec *rem);
&&&Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
&&&&&&&nanosleep(): _POSIX_C_SOURCE >= 199309L既然有了能精确到纳秒的nanosleep可用,上面的较低精度的函数也就可以休息了。实际上在Linux系统下,sleep和usleep就是通过一个系统调用nanosleep实现的。用带有超时功能的API变相实现睡眠如果开发者不知道有usleep和nanosleep,这个时候他可能会联想到select类的系统调用:
& & && According to POSIX.1-2001 */
&&&&&&&#include <sys/select.h>
&&&&&&&/* According to earlier standards */
&&&&&&&#include <sys/time.h>
&&&&&&&#include <sys/types.h>
&&&&&&&#include <unistd.h>
&&&&&&&int select(int nfds, fd_set *readfds, fd_set *writefds,
&&&&&&&&&&&&&&&&&&fd_set *exceptfds, struct timeval *timeout);&&&&&
#include <poll.h>
&&&&&&&int poll(struct pollfd *fds, nfds_t nfds, int timeout);&&&&&&
#include <sys/epoll.h>
&&&&&&&int epoll_wait(int epfd, struct epoll_event *events,
&&&&&&&&&&&&&&&&&&&&&&int maxevents, int timeout);
&&&&&&&int epoll_pwait(int epfd, struct epoll_event *events,
&&&&&&&&&&&&&&&&&&&&&&int maxevents, int timeout,
&&&&&&&&&&&&&&&&&&&&&&const sigset_t *sigmask);从函数原型和相关手册来看,poll和epoll_wait能提供的时间精度为毫秒,select比他们两个略胜一筹,为微秒,和前述的usleep相当。但是,果真如此么?这需要我们深入到Linux的具体实现,在内核里,这几个系统调用的超时功能都是通过内核中的动态定时器实现的,而动态定时器的时间精度是由当前内核的HZ数决定的。如果内核的HZ是100,那么动态定时器的时间精度就是1/HZ=1/100=10毫秒。目前,X86系统的HZ最大可以定义为1000,也就是说X86系统的动态定时器的时间精度最高只能到1毫秒。由此来看,select用来指示超时的timeval数据结构,只是看起来很美,实际上精度和poll/epoll_wait相当。基于定时器的实现除了基于sleep的实现外,还有基于能用信号进行异步提醒的定时器实现:#include <stdio.h>
#include <signal.h>
int main(int argc, char *argv[])
&&&&&&&&sigset_t block;
&&&&&&&&sigemptyset(&block);
&&&&&&&&sigaddset(&block, SIGALRM);
&&&&&&&&sigprocmask(SIG_BLOCK, &block, NULL);
&&&&&&&&while (1) {
&&&&&&&&&&&&&&&&printf("%d\n", time(NULL));
&&&&&&&&&&&&&&&&alarm(2);
&&&&&&&&&&&&&&&&sigwaitinfo(&block, NULL);
&&&&&&&&return 0;
}显然,上面的代码并没有利用信号进行异步提醒,而是通过先阻塞信号的传递,然后用sigwaitinfo等待并将信号取出的方法将异步化同步。这样做的目的是为了尽可能减少非必要的信号调用消耗,因为这个程序只需要执行这个简单的单一任务,所以异步除了带来消耗外,并无任何好处。读者可能已经发现上面的代码无非是把最初的代码中的sleep换成了alarm和sigwaitinfo两个调用,除了复杂了代码之外,好像并没有什么额外的好处。alarm的时间精度只能到1s,并且alarm和sigwaitinfo的确也可以看成是sleep的一种实现,实际上有的sleep确实是透过alarm来实现的,请看sleep的手册页:BUGS&&&&&& sleep()& may be implemented using SIGALRM; mixing calls to alarm(2) and&&&&&& sleep() is a bad idea.&&&&&& Using longjmp(3) from a signal handler or& modifying& the& handling& of&&&&&& SIGALRM while sleeping will cause undefined results.但是,这只是表象,本质他们是不同的,sleep是拨了一个临时实时定时器并等待定时器到期,而alarm是用进程唯一的实时定时器来定时唤醒等待信号到来的进程执行。如果需要更高的时间精度,可以采用精度为微秒的alarm版本ualarm:&&&&&& #include <unistd.h>
&&&&&&&useconds_t ualarm(useconds_t usecs, useconds_t interval);
&&&Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
&&&&&&&ualarm(): _BSD_SOURCE || _XOPEN_SOURCE >= 500或者是直接用setitimer操纵进程的实时定时器:&&&&&
#include <sys/time.h>
&&&&&&&int getitimer(int which, struct itimerval *value);
&&&&&&&int setitimer(int which, const struct itimerval *value,
&&&&&&&&&&&&&&&&&&&&&struct itimerval *ovalue);细心的你应该已经注意到了,ualarm和setitimer都额外提供了间隔时间的设置以便于间隔定时器用SIGALRM周期性的唤醒进程,这对于我们的需求有什么意义呢?请听我慢慢道来。一般来说,需要定时执行的任务所消耗的时间都很短,至少都会少于间隔时间,否则这个需求就是无法实现的。我们前面的程序实现,都是假设任务消耗时间为0,实际上的任务并不总是像打印当前系统时间这么简单,即便它们持续的时间真的短到相对来说可以忽略不计,如果这些小的忽略不计累积起来,也还是可能会造成长时间后的大偏差,所以我们有必要将这段时间计算进来。一种补救的措施是在任务执行的前后执行gettimeofday得到系统的时间,然后做差得到任务消耗时间并在接下来的“sleep”中将其扣除。问题看似解决了,但是我们毕竟没有将系统进行上下文切换的时间和计算消耗时间的时间考虑进来,这样的话,还是会存在较大的误差。另一种计算量相对小些的算法是:直接通过时间间隔计算下一次超时的绝对时间,然后根据当前的绝对时间算出需要等待的时间并睡眠。但是,这也只是修修补补而已,并没有从根本上解决问题。间隔定时器的出现从根本上解决了上面所提的问题,它自身就提供周期唤醒的功能,从而避免了每次都计算的负担。因为ualarm已经被放弃,所以用setitimer再次改写代码:
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
int main(int argc, char *argv[])
&&&&&&&&sigset_t block;
&&&&&&&&struct itimerval itv;
&&&&&&&&sigemptyset(&block);
&&&&&&&&sigaddset(&block, SIGALRM);
&&&&&&&&sigprocmask(SIG_BLOCK, &block, NULL);
&&&&&&&&itv.it_interval.tv_sec = 2;
&&&&&&&&itv.it_interval.tv_usec = 0;
&&&&&&&&itv.it_value = itv.it_interval;
&&&&&&&&setitimer(ITIMER_REAL, &itv, NULL);
&&&&&&&&while (1) {
&&&&&&&&&&&&&&&&printf("%d\n", time(NULL));
&&&&&&&&&&&&&&&&sigwaitinfo(&block, NULL);
&&&&&&&&return 0;
}进程的间隔计时器能够提供的时间精度为微秒,对于大多数的应用来说,应该已经足够,如果需要更高的时间精度,或者需要多个定时器,那么每个进程一个的实时间隔定时器就无能为力了,这个时候我们可以选择POSIX实时扩展中的定时器:&&&&& #include <signal.h>
&&&&&&&#include <time.h>
&&&&&&&int timer_create(clockid_t
struct sigevent
&&&&&&&&&&&&&&timer_t
&&&&&&&int
timer_getoverrun(timer_t timerid);
&&&&&&&int
timer_gettime(timer_t timerid, struct itimerspec *value);
&&&&&&&int
timer_settime(timer_t timerid, int flags,
&&&&&&&&&&&&&&const struct itimerspec
&&&&&&&&&&&&&&struct itimerspec
ovalue);它实际上就是进程间隔定时器的增强版,除了可以定制时钟源(nanosleep也存在能定制时钟源的版本:clock_nanosleep)和时间精度提高到纳秒外,它还能通过将evp->sigev_notify设定为如下值来定制定时器到期后的行为:SIGEV_SIGNAL: 发送由evp->sigev_sino指定的信号到调用进程,evp->sigev_value的值将被作为siginfo_t结构体中si_value的值。SIGEV_NONE:什么都不做,只提供通过timer_gettime和timer_getoverrun查询超时信息。SIGEV_THREAD: 以evp->sigev_notification_attributes为线程属性创建一个线程,在新建的线程内部以evp->sigev_value为参数调用evp->sigev_notification_function。SIGEV_THREAD_ID:和SIGEV_SIGNAL类似,不过它只将信号发送到线程号为evp->sigev_notify_thread_id的线程,注意:这里的线程号不一定是POSIX线程号,而是线程调用gettid返回的实际线程号,并且这个线程必须实际存在且属于当前的调用进程。更新后的程序如下(需要连接实时扩展库: -lrt):#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <errno.h>
#include <sched.h>
main(int argc, char *argv[])
&&&&&&&&timer_t
&&&&&&&&struct itimerspec
&&&&&&&&sigset_t
&&&&&&&&struct sched_param
&&&&&&&&sigemptyset(&block);
&&&&&&&&sigaddset(&block, SIGALRM);
&&&&&&&&sigprocmask(SIG_BLOCK, &block, NULL);
&&&&&&&&timer_create(CLOCK_MONOTONIC, NULL, &timer);
&&&&&&&&timeout.it_interval.tv_sec = 2;
&&&&&&&&timeout.it_interval.tv_nsec = 0;
&&&&&&&&timeout.it_value = timeout.it_interval;
&&&&&&&&timer_settime(timer, 0, &timeout, NULL);
&&&&&&&&while (1) {
&&&&&&&&&&&&&&&&fprintf(stderr, "%d\n", time(NULL));
&&&&&&&&&&&&&&&&sigwaitinfo(&block, NULL);
&&&&&&&&return 0;
}至于时钟源为什么是CLOCK_MONOTONIC而不是CLOCK_REALTIME,主要是考虑到系统的实时时钟可能会在程序运行过程中更改,所以存在一定的不确定性,而CLOCK_MONOTONIC则不会,较为稳定。至此为止,我们已经找到了目前Linux提供的精度最高的定时器API,它应该能满足大多数情况的要求了。其它问题传统信号的不可靠性传统UNIX信号是不可靠的,也就是说如果当前的信号没有被处理,那么后续的同类信号将被丢失,而不是被排队,而实时信号则没有这个问题,它是被排队的。联系到当前应用,如果信号丢失,则是因为任务消耗了过多的处理器时间,而这个不确定性是那个任务带来的,需要改进的应该是那个任务。系统负载过高如果系统的负载过高,使得我们的程序因为不能得到及时的调度导致时间精度降低,我们不妨通过nice提高当前程序的优先级,必要时可以通过sched_setscheduler将当前进程切换成优先级最高的实时进程已确保得到及时调度。硬件相关的问题硬件配置也极大的影响着定时器的精度,有的比较老的遗留系统可能没有比较精确的硬件定时器,那样的话我们就无法期待它能提供多高的时钟精度了。相反,如果系统的配置比较高,比如说对称多处理系统,那么即使有的处理器负载比较高,我们也能通过将一个处理器单独分配出来处理定时器来提高定时器的精度。更高的时间精度虽然,Linux的API暗示它能够提供纳秒级的时间精度,但是,由于种种不确定因素,它实际上并不能提供纳秒级的精度,比较脆弱。如果你需要更高强度的实时性,请考虑采用软实时系统、硬实时系统、专有系统,甚至是专业硬件。注意:为了简便,以上所有代码都没有出错处理,请读者在现实的应用中自行加入出错处理,以提高程序的健壮性。尤其注意sleep类的返回值,它们可能没到期就返回,这个时候你应该手动计算需要再睡眠多长才能满足原始的睡眠时间要求,如果该API并没有返回剩余的时间的话。参考资料:Linux在线手册Linux内核源码
阅读(11374) | 评论(5) | 转发(5) |
相关热门文章
给主人留下些什么吧!~~
现在才看到你的这篇博文,真是相见恨晚。请问关于实时扩展库,那个可以找到相关的资料?一般的内核里会有吗?我是想用在ARM&板上,如果有能到微秒级的定时就足够了。
这个需要看你的内核版本是否支持hrtimer和硬件是否有提供高精度的时钟源。
自己在PC上试验了一下文中的代码,文中前面提到 “目前,X86系统的HZ最大可以定义为1000,也就是说X86系统的动态定时器的时间精度最高只能到1毫秒”
那么,后面关于纳秒级更高精度的定时器是怎么回事呢,我在试验中把定时器的时间间隔设置为小于1ms的时候,得到的都是1K 的频率。大于 1ms就是正确的。
我想请问下,在PC x86平台下如何实现高精度的时钟呢?
兄弟写的经典
顶。。。。
请登录后评论。}

我要回帖

更多关于 android 定时器 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信