是否有任何表现良好的 POSIX 间隔计时器? [英] Are there any well-behaved POSIX interval timers?

查看:23
本文介绍了是否有任何表现良好的 POSIX 间隔计时器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

受到最后一个闰秒的启发,我一直在探索使用 POSIX 调用的计时(特别是间隔计时器).

Inspired by the last leap second, I've been exploring timing (specifically, interval timers) using POSIX calls.

POSIX 提供了几种设置定时器的方法,但它们都有问题:

POSIX provides several ways to set up timers, but they're all problematic:

  • sleepnanosleep——它们在被信号中断后重新启动很烦人,并且会引入时钟偏差.您可以通过一些额外的工作来避免部分(但不是全部)这种偏差,但这些函数使用实时时钟,因此这并非没有缺陷.
  • setitimer 或更现代的 timer_settime——这些被设计为间隔计时器,但它们是每个进程的,如果您需要多个活动计时器,这是一个问题.它们也不能同步使用,但这没什么大不了的.
  • clock_gettimeclock_nanosleepCLOCK_MONOTONIC 一起使用时似乎是正确的答案.clock_nanosleep 支持绝对超时,所以你可以只睡觉,增加超时,然后重复.以这种方式在中断后重新启动也很容易.不幸的是,这些功能也可能是 Linux 特有的:Mac OS X 或 FreeBSD 不支持它们.
  • pthread_cond_timedwait 在 Mac 上可用,并且可以与 gettimeofday 一起使用作为一种笨拙的解决方法,但在 Mac 上它只能与实时时钟一起使用,因此它受制于系统时钟设置或闰秒发生时的不当行为.
  • sleep and nanosleep—these are annoying to restart after they're interrupted by a signal, and they introduce clock skew. You can avoid some, but not all, of this skew with some extra work, but these functions use the realtime clock, so this isn't without pitfalls.
  • setitimer or the more modern timer_settime—these are designed to be interval timers, but they're per-process, which is a problem if you need multiple active timers. They also can't be used synchronously, but that's less of a big deal.
  • clock_gettime and clock_nanosleep seem like the right answer when used with CLOCK_MONOTONIC. clock_nanosleep supports absolute timeouts, so you can just sleep, increment the timeout, and repeat. It's easy to restart after an interruption that way, too. Unfortunately, these functions might as well be Linux-specific: there's no support for them on Mac OS X or FreeBSD.
  • pthread_cond_timedwait is available on the Mac and can work with gettimeofday as a kludgy workaround, but on the Mac it can only work with the realtime clock, so it's subject to misbehavior when the system clock is set or a leap second happens.

是否有我遗漏的 API?是否有一种合理可移植的方法可以在类 UNIX 系统上创建性能良好的间隔计时器,或者这是否总结了当今的状况?

Is there an API I'm missing? Is there a reasonably portable way to create well-behaved interval timers on UNIX-like systems, or does this sum up the state of things today?

我的意思是:行为端正和合理便携:

By well-behaved and reasonably portable, I mean:

  • 不易出现时钟偏差(当然,系统时钟本身的偏差要减去)
  • 对设置的系统时钟或闰秒具有弹性
  • 能够在同一个进程中支持多个定时器
  • 至少在 Linux、Mac OS X 和 FreeBSD 上可用

关于闰秒的说明(回应 R.. 的回答):

POSIX 天的长度正好是 86,400 秒,但现实世界中的天很少会更长或更短.系统如何解决这种差异是实现定义的,但闰秒与前一秒共享相同的 UNIX 时间戳是很常见的.另请参阅:闰秒以及如何处理它们.

POSIX days are exactly 86,400 seconds long, but real-world days can rarely be longer or shorter. How the system resolves this discrepancy is implementation-defined, but it's common for the leap second to share the same UNIX timestamp as the previous second. See also: Leap Seconds and What To Do With Them.

Linux 内核闰秒错误是由于将时钟调回一秒后未能进行内务处理的结果:https://lkml.org/lkml/2012/7/1/203.即使没有那个错误,时钟也会向后跳一秒.

The Linux kernel leap second bug was a result of failing to do housekeeping after setting the clock back a second: https://lkml.org/lkml/2012/7/1/203. Even without that bug, the clock would have jumped backwards one second.

推荐答案

kqueuekevent 可用于此目的.OSX 10.6 和 FreeBSD 8.1 添加了对 EVFILT_USER 的支持,我们可以使用它从另一个线程唤醒事件循环.

kqueue and kevent can be utilized for this purpose. OSX 10.6 and FreeBSD 8.1 add support for EVFILT_USER, which we can use to wake up the event loop from another thread.

注意,如果你用它来实现你自己的条件和timedwait,你不需要锁以避免竞争条件,这与这个很好的答案,因为您不能错过"队列中的事件.

Note that if you use this to implement your own condition and timedwait, you do not need locks in order to avoid race conditions, contrary to this excellent answer, because you cannot "miss" an event on the queue.

使用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
");

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

    fprintf(stderr, "thread wakeup
");

    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
");
    pthread_t thread;
    if (pthread_create(&thread, NULL, run_thread, NULL))
        diep("pthread_create");

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

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

    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

这篇关于是否有任何表现良好的 POSIX 间隔计时器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆