【问题标题】:How to make a C timer expire at a particular system time in Linux如何使 C 计时器在 Linux 中的特定系统时间到期
【发布时间】:2014-08-06 04:31:43
【问题描述】:

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

所以我计算了 CLOCK_REALTIME 和 CLOCK_MONOTONIC 时钟在 2 个定时器到期之间经过的时间。它显示 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 标识。它的时间表示自纪元以来的秒和纳秒。 当其时间改变时,相对间隔的计时器不受影响,但绝对时间点的计时器受到影响。

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

【问题讨论】:

    标签: c linux time timer clock


    【解决方案1】:

    timer_settime 的手册页中可以找到相同的规则,并附有一些附加说明:

    默认情况下,指定的初始过期时间 new_value-&gt;it_value 相对于当前时间解释 呼叫时计时器的时钟。这可以通过修改 在标志中指定 TIMER_ABSTIME,在这种情况下为 new_value->it_value 被解释为在定时器时钟上测量的绝对值; 即当时钟值达到该值时,定时器将超时 由new_value-&gt;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);
    

    【讨论】:

    • 你能解释一下为什么我们应该使用gettimeofday(&amp;tv, NULL);。仅使用 TIMER_ABSTIME 标志对我来说就可以了。而且我认为使用TIMER_ABSTIME 标志和CLOCK_MONOTONIC 计时器根本不会产生任何影响。我说的对吗?
    • @MrPavanayi:使用TIMER_ABSTIME,第一个到期时间需要从当前时间开始计算。可以在不使用当前时间的情况下设置过去的时间,但这不会重现问题中代码的行为。实际上,最好使用clock_gettime,已修复。
    【解决方案2】:

    试试这个:

    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);
    

    【讨论】:

    • 虽然欢迎使用此代码 sn-p,并且可能会提供一些帮助,但它会是 greatly improved if it included an explanation of howwhy 这解决了问题。请记住,您正在为将来的读者回答问题,而不仅仅是现在提问的人!请edit您的答案添加解释,并说明适用的限制和假设。
    猜你喜欢
    • 2020-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-10
    • 1970-01-01
    • 2014-04-20
    相关资源
    最近更新 更多