如何使C计时器在Linux中的特定系统时间到期

时间:2014-08-06 04:31:43

标签: c linux time timer clock

我正在尝试创建一个取决于系统时钟的计时器。这意味着当系统时间改变时,它也会影响该计时器的到期时间。所以我想,创建一个基于CLOCK_REALTIME的计时器应该可以解决问题。但是当这个定时器在60秒后到期并且我将系统时钟提前32秒(使用日期命令)时,定时器在60秒后完全到期。它没有在32秒前过期。

所以我计算了2个定时器到期之间CLOCK_REALTIME和CLOCK_MONOTONIC时钟的经过时间。 CLOCK_REALTIME显示92秒,CLOCK_MONOTONIC显示60秒,这让我感到惊讶,基于CLOCK_REALTIME的计时器没有查找系统时钟变化。任何人都可以解释一下这种行为吗?

#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <string.h>
#include <sys/time.h>

timer_t timerID;
struct timespec rt1, rt2, mt1, mt2;

void TimerCalback()
{
  clock_gettime(CLOCK_REALTIME, &rt1);
  clock_gettime(CLOCK_MONOTONIC, &mt1);
  printf("%lu sec elapsed for CLOCK_REALTIME\n", rt1.tv_sec - rt2.tv_sec);
  printf("%lu sec elapsed for CLOCK_MONOTONIC\n", mt1.tv_sec - mt2.tv_sec);

  rt2 = rt1;
  mt2 = mt1;

  time_t rawtime;
  struct tm * timeinfo;
  time ( &rawtime );
  timeinfo = localtime ( &rawtime );
  printf("REALTIME Timer Expired at %s\n", asctime (timeinfo));
}

void CreateTimer()
{
  struct sigaction sa;
  sa.sa_flags     = SA_SIGINFO;
  sa.sa_sigaction = TimerCalback;

  sigemptyset(&sa.sa_mask);
  sigaction(SIGRTMIN, &sa, NULL);
  struct sigevent te;
  memset(&te,0,sizeof(struct sigevent));

  te.sigev_notify          = SIGEV_SIGNAL;
  te.sigev_signo           = SIGRTMIN;
  te.sigev_value.sival_ptr = &timerID;
  timer_create(CLOCK_REALTIME, &te, &timerID);

  struct itimerspec its;
  its.it_value.tv_sec     = 1;
  its.it_value.tv_nsec    = 0;

  its.it_interval.tv_sec  = 60;
  its.it_interval.tv_nsec = 0;

  timer_settime(timerID, 0, &its, NULL);
}

void main()
{
  CreateTimer();
  while(1)
  {
    usleep(1);
  }
}

我得到了这个输出。第一次到期后,我提出了系统时钟。

$ ./realtimeTimer
1407240463 sec elapsed for CLOCK_REALTIME
17747 sec elapsed for CLOCK_MONOTONIC
REALTIME Timer Expired at Tue Aug  5 17:37:43 2014

92 sec elapsed for CLOCK_REALTIME
60 sec elapsed for CLOCK_MONOTONIC
REALTIME Timer Expired at Tue Aug  5 17:39:15 2014  

后来在网上搜索,我偶然发现this man page告诉

  

所有实现都支持系统范围的实时时钟,由CLOCK_REALTIME标识。它的时间代表自纪元以来的秒和纳秒。 当时间更改时,相对间隔的计时器不受影响,但绝对时间点的计时器会受到影响。

这可能是造成这种行为的原因吗? 我在做什么错误/在这里失踪?
有没有办法让计时器在特定的系统时间到期?

2 个答案:

答案 0 :(得分:2)

同样的规则可以在timer_settime的联机帮助页中找到,并附加一些解释:

  

默认情况下,指定的初始到期时间          new_value->it_value相对于当前时间进行解释          通话时的计时器时钟。这可以修改          在flags中指定 TIMER_ABSTIME ,在这种情况下new_value-&gt; it_value          被解释为在计时器时钟上测量的绝对值;          也就是说,当时钟值达到该值时,定时器将到期          由new_value->it_value指定。如果指定的绝对时间有          已经通过,然后计时器立即到期,并且超限          count(请参阅timer_getoverrun(2))将正确设置。

     

如果在调整时调整了 CLOCK_REALTIME 时钟的值          基于该时钟的绝对定时器被设防,然后到期          计时器将适当调整。调整           CLOCK_REALTIME 时钟对基于此的相对计时器没有影响          时钟。

是的,您一直在设置相对计时器,这就是为什么它忽略了对系统时间的调整。

您可以尝试以下方法:

struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);

struct itimerspec its;
its.it_value.tv_sec     = ts.tv_sec + 1;
its.it_value.tv_nsec    = ts.tv_nsec;
its.it_interval.tv_sec  = 60;
its.it_interval.tv_nsec = 0;
timer_settime(timerID, TIMER_ABSTIME, &its, NULL);

答案 1 :(得分:0)

试试这个:

struct timeval tv;
gettimeofday(&tv, NULL);

struct itimerspec its;
its.it_value.tv_sec     = 1;
its.it_value.tv_nsec    = 0;
its.it_interval.tv_sec  = tv_sec + 60;
its.it_interval.tv_nsec = tv_nsec;
timer_settime(timerID, TIMER_ABSTIME, &its, NULL);