是否有任何乖巧的POSIX间隔计时器?(Are there any well-behaved POS

2019-06-26 02:01发布

由最后的闰秒的启发,我使用POSIX调用一直在探索的定时(即,间隔计时器)。

POSIX提供了几种方法来设置定时器,但他们都存在问题:

  • sleepnanosleep -这些都是烦人他们一个信号中断后重新启动,他们引进的时钟偏移。 你可以避开一些,但不是全部,这与歪斜一些额外的工作,但这些功能使用实时时钟,所以这也不是没有缺陷。
  • setitimer或更现代的timer_settime -这些被设计为间隔定时器,但他们每个过程,这是一个问题,如果你需要多个活动的定时器。 他们也不能使用同步,但这是少了大事。
  • clock_gettimeclock_nanosleep当用于看似正确答案CLOCK_MONOTONICclock_nanosleep支持绝对超时,所以你可以睡,增加超时,并重复。 这很容易中断这种方式后,重新启动了。 不幸的是,这些功能可能会成为Linux特有:有在Mac OS X或者FreeBSD没有他们的支持。
  • pthread_cond_timedwait可在Mac上,可以一起工作gettimeofday的缺憾解决办法,但在Mac上只能与实时时钟工作,所以它的主题时,系统时钟设置或闰秒发生的不当行为。

是否有我丢失的API? 有没有在类UNIX系统上创建乖巧间隔定时器一个合理的可移植的方式,还是这个总结的东西今天的状态?

通过规矩,合理便携式,我的意思是:

  • 不容易出现时钟偏差(负,当然,系统时钟本身的偏移)
  • 弹性的以系统时钟被设置或被一个闰秒发生
  • 能够支持多个计时器在同一进程
  • 可至少在Linux,Mac OS X和FreeBSD

上闰秒的说明 (响应于R.,的回答 ):

POSIX天正是86400秒长,但现实世界的日子很少能长或短。 系统如何解决这种差异是实现定义的,但它是常见的闰秒共享相同的UNIX时间戳之前的第二位。 另请参见: 闰秒和什么与他们无关 。

Linux内核闰秒的错误是没有设置时钟拨回一秒钟后,以做家政的结果: https://lkml.org/lkml/2012/7/1/203 。 即使没有错误,时钟跳下去向后一秒钟。

Answer 1:

POSIX定时器( timer_create )不需要的信号; 你也可以安排计时器到期时在一个线程中通过交付SIGEV_THREAD通知类型。 不幸的glibc的实现实际上会为每个到期一个新的线程(两者有很多的开销和破坏的实时质量稳健性的希望),尽管该标准允许每个到期同一线程的重用。

简短的说,我只想建议让自己的线程使用clock_nanosleepTIMER_ABSTIMECLOCK_MONOTONIC的间隔定时器。 既然你提到了一些破碎的系统可能缺乏这些接口,你可以简单地有一个下拉的实现(例如基于pthread_cond_timedwait在这样的系统),并计算它可能是品质较低,由于缺乏单调时钟,但是,这是只使用低质量实行类似的MacOSX的基本限制。

至于你提到闰秒的关注,如果NTPD或类似的是使您的实时时钟向后跳的时候,一个闰秒发生,这是在NTPD一个严重的错误。 POSIX时间(从epoch秒)是在每标准日历秒(一天的准确八万六千四分之一),而不是SI秒为单位,因此,唯一的地方跨越第二逻辑所属的POSIX系统上(如果任何地方)处于mktime / gmtime / localtime时,他们之间的转换time_t和破旧的时间。 我一直没下袭击这一次的错误,但他们似乎是由于系统软件做了很多愚蠢的错误和东西,不是从任何根本性的问题。



Answer 2:

kqueuekevent可以用于此目的。 OSX 10.6和FreeBSD 8.1新增支持EVFILT_USER ,我们可以用它来唤醒从另一个线程的事件循环。

请注意,如果你用它来实现自己的条件和timedwait,你不需要锁,以避免竞争状态,违背了这个优秀的答案 ,因为你不能“错过”的队列中的事件。

资料来源:

  • FreeBSD的手册页
  • OS X man页面
  • kqueue的教程
  • libevent的源代码

示例代码

与编译clang -o test -std=c99 test.c

#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

// arbitrary number used for the identifier property
const int NOTIFY_IDENT = 1337;

static int kq;

static void diep(const char *s) {
   perror(s);
   exit(EXIT_FAILURE);
}

static void *run_thread(void *arg) {
    struct kevent kev;
    struct kevent out_kev;
    memset(&kev, 0, sizeof(kev));
    kev.ident = NOTIFY_IDENT;
    kev.filter = EVFILT_USER;
    kev.flags = EV_ADD | EV_CLEAR;

    struct timespec timeout;
    timeout.tv_sec = 3;
    timeout.tv_nsec = 0;

    fprintf(stderr, "thread sleep\n");

    if (kevent(kq, &kev, 1, &out_kev, 1, &timeout) == -1)
        diep("kevent: waiting");

    fprintf(stderr, "thread wakeup\n");

    return NULL;
}

int main(int argc, char **argv) {
    // create a new kernel event queue
    kq = kqueue();
    if (kq == -1)
        diep("kqueue()");


    fprintf(stderr, "spawn thread\n");
    pthread_t thread;
    if (pthread_create(&thread, NULL, run_thread, NULL))
        diep("pthread_create");

    if (argc > 1) {
        fprintf(stderr, "sleep for 1 second\n");
        sleep(1);
        fprintf(stderr, "wake up thread\n");

        struct kevent kev;
        struct timespec timeout = { 0, 0 };

        memset(&kev, 0, sizeof(kev));
        kev.ident = NOTIFY_IDENT;
        kev.filter = EVFILT_USER;
        kev.fflags = NOTE_TRIGGER;

        if (kevent(kq, &kev, 1, NULL, 0, &timeout) == -1)
            diep("kevent: triggering");
    } else {
        fprintf(stderr, "not waking up thread, pass --wakeup to wake up thread\n");
    }

    pthread_join(thread, NULL);
    close(kq);
    return EXIT_SUCCESS;
}

产量

$ time ./test
spawn thread
not waking up thread, pass --wakeup to wake up thread
thread sleep
thread wakeup

real    0m3.010s
user    0m0.001s
sys 0m0.002s

$ time ./test --wakeup
spawn thread
sleep for 1 second
thread sleep
wake up thread
thread wakeup

real    0m1.010s
user    0m0.002s
sys 0m0.002s


Answer 3:

你可以看一下这个问题在这里为clock_gettime仿真,我还提供了一个答案,而是帮助我为好。 我最近增加了一个简单的定时器,以一个小仓库我一直对Mac OS X的时序是部分模拟POSIX调用。 一个简单的测试运行在2000Hz的计时器。 回购协议被称为PosixMachTiming 。 试试看。

PosixMachTiming是基于马赫 。 看来一些计时相关马赫的API已经从苹果公司的网页消失了,已经过时了,但仍有漂浮的源代码位。 它看起来像AbsoluteTime单位和内核抽象发现这里是做事情的新方式。 反正在PosixMachTiming回购仍然为我工作。

PosixMachTiming概述

clock_gettime被模拟为CLOCK_REALTIME由马赫函数调用自来水进入系统实时时钟,被称为CALENDAR_CLOCK

所述clock_gettime被仿真为CLOCK_MONOTONIC通过使用全局变量( extern mach_port_t clock_port )。 计算机可以打开或可能被唤醒时,这个时钟被初始化。 我不确定。 在任何情况下,它的全局变量,函数mach_absolute_time()调用。

clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, ...)通过使用模拟nanosleep当前时间和绝对单调时间之间的差。

itimer_start()itimer_step()是基于呼叫clock_nanosleep的目标绝对单调的时间。 它通过递增在每次迭代(不是当前时间),使得时钟偏移不是问题的时间步长的目标时间。

请注意,这还不能满足你的要求是能够支持多个计时器在相同的过程。



文章来源: Are there any well-behaved POSIX interval timers?
标签: c timer posix