gpt4 book ai didi

c - 是否有任何行为良好的 POSIX 间隔计时器?

转载 作者:太空狗 更新时间:2023-10-29 16:28:52 26 4
gpt4 key购买 nike

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

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

  • sleepnanosleep — 这些在被信号中断后重新启动很烦人,并且会引入时钟偏差。您可以通过一些额外的工作来避免部分(但不是全部)这种偏差,但这些函数使用实时时钟,因此这并非没有缺陷。
  • setitimer 或更现代的 timer_settime — 这些被设计为间隔计时器,但它们是每个进程的,如果您需要多个事件计时器,这是一个问题.它们也不能同步使用,但这没什么大不了的。
  • clock_gettimeclock_nanosleepCLOCK_MONOTONIC 一起使用时似乎是正确的答案。 clock_nanosleep 支持绝对超时,因此您可以休眠、增加超时并重复。以这种方式在中断后重新启动也很容易。不幸的是,这些功能也可能是特定于 Linux 的:在 Mac OS X 或 FreeBSD 上不支持它们。
  • pthread_cond_timedwait 在 Mac 上可用并且可以与 gettimeofday 一起使用作为一种笨拙的解决方法,但在 Mac 上它只能与实时时钟一起使用,因此它受制于设置系统时钟或发生闰秒时的不当行为。

是否缺少某个 API?是否有一种合理的可移植方式来在类 UNIX 系统上创建行为良好的间隔计时器,或者这是否总结了当今的状态?

我所说的行为良好和合理便携的意思是:

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

关于闰秒的注释(对 R..'s answer 的回应):

POSIX 天数恰好是 86,400 秒长,但现实世界中的天数很少会更长或更短。系统如何解决这种差异是实现定义的,但闰秒与前一秒共享相同的 UNIX 时间戳是很常见的。另请参阅:Leap Seconds and What To Do With Them .

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

最佳答案

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

请注意,如果您使用它来实现您自己的条件和 timedwait,您不需要锁来避免竞争条件,这与 this excellent answer 相反,因为您不能“错过”队列中的事件。

来源:

示例代码

编译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

关于c - 是否有任何行为良好的 POSIX 间隔计时器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11338899/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com