在C++/Linux中设置单调时钟的一些技巧
Some hack to set the monotonic clock in C++ / Linux
Linux C++问题,他们说你不能设置单调时钟。我想设置单调时钟。Linux中的C++有什么破解或技巧可以做到这一点吗?我想使用clock_settime(clock_MONOTONC,&ts(并让它将时钟设置为ts中指定的时间,或者使用clock.settime以外的其他方法来完成同样的事情?我对精心设计的技巧或变通方法很冷静。
- 阅读
- 从您希望的值中减去当前值
- 将来,添加此偏移
将clock_gettime()
和clock_settime()
与自己的插入。
这是一个经过测试的工作示例,应该是线程安全的。首先,libmonoton.c本身:
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
#include <stdlib.h>
#include <dlfcn.h>
#include <time.h>
#include <errno.h>
#ifndef ENV_OFFSET_NAME
#define ENV_OFFSET_NAME "LIBMONOTONIC_OFFSET"
#endif
typedef int (*clock_gettime_funcptr)(clockid_t, struct timespec *);
typedef int (*clock_settime_funcptr)(clockid_t, const struct timespec *);
static volatile clock_gettime_funcptr original_clock_gettime = NULL;
static volatile clock_settime_funcptr original_clock_settime = NULL;
static volatile long offset_sec = 0;
static volatile long offset_nsec = 0;
/* Call original clock_gettime(). */
static inline int do_clock_gettime(clockid_t clk_id, struct timespec *tp)
{
clock_gettime_funcptr original = __atomic_load_n(&original_clock_gettime, __ATOMIC_SEQ_CST);
if (!original) {
original = dlsym(RTLD_NEXT, "clock_gettime");
if (!original) {
errno = EINVAL;
return -1;
}
}
return original(clk_id, tp);
}
/* Call original clock_settime(). */
static inline int do_clock_settime(clockid_t clk_id, const struct timespec *tp)
{
clock_settime_funcptr original = __atomic_load_n(&original_clock_settime, __ATOMIC_SEQ_CST);
if (!original) {
original = dlsym(RTLD_NEXT, "clock_settime");
if (!original) {
errno = EINVAL;
return -1;
}
}
return original(clk_id, tp);
}
/* Interposed version of clock_settime(). */
int clock_settime(clockid_t clk_id, const struct timespec *tp)
{
if (clk_id != CLOCK_MONOTONIC)
return do_clock_settime(clk_id, tp);
struct timespec now;
if (do_clock_gettime(CLOCK_MONOTONIC, &now) == -1)
return -1;
long off_sec = tp->tv_sec - now.tv_sec;
long off_nsec = tp->tv_nsec - now.tv_nsec;
if (off_nsec <= -1000000000) {
off_sec -= (-off_nsec) / 1000000000;
off_nsec = -((-off_nsec) % 1000000000);
}
if (off_nsec >= 1000000000) {
off_sec += off_nsec / 1000000000;
off_nsec = off_nsec % 1000000000;
}
do {
__atomic_store_n(&offset_sec, off_sec, __ATOMIC_SEQ_CST);
__atomic_store_n(&offset_nsec, off_nsec, __ATOMIC_SEQ_CST);
} while (offset_sec != off_sec || offset_nsec != off_nsec);
return 0;
}
int clock_gettime(clockid_t clk_id, struct timespec *tp)
{
if (clk_id != CLOCK_MONOTONIC)
return do_clock_gettime(clk_id, tp);
struct timespec now;
long off_sec, off_nsec;
if (do_clock_gettime(clk_id, &now) == -1)
return -1;
do {
off_sec = __atomic_load_n(&offset_sec, __ATOMIC_SEQ_CST);
off_nsec = __atomic_load_n(&offset_nsec, __ATOMIC_SEQ_CST);
} while (off_sec != offset_sec || off_nsec != offset_nsec);
now.tv_sec += off_sec;
now.tv_nsec += off_nsec;
if (now.tv_nsec < 0) {
const long nsec = -now.tv_nsec;
now.tv_sec -= 1 + nsec / 1000000000;
now.tv_nsec = 1000000000 - (nsec % 1000000000);
}
if (now.tv_nsec >= 1000000000) {
now.tv_sec += now.tv_nsec / 1000000000;
now.tv_nsec = now.tv_nsec % 1000000000;
}
*tp = now;
return 0;
}
static void monotonic_init(void) __attribute__((constructor));
static void monotonic_init(void)
{
const int saved_errno = errno;
const char *value = getenv(ENV_OFFSET_NAME);
if (value && *value)
do {
long off_sec = 0, off_nsec = 0;
long decimal = 100000000;
int negative = 0;
/* Skip leading whitespace. */
while (*value == 't' || *value == 'n' || *value == 'v' ||
*value == 'f' || *value == 'r' || *value == ' ')
value++;
/* Parse signs. */
while (*value == '+' || *value == '-')
if (*(value++) == '-')
negative = !negative;
/* Require first digit. */
if (*value >= '0' && *value <= '9')
off_sec = (*(value++) - '0');
else
break;
/* Parse the rest of the integer digits. */
while (*value >= '0' && *value <= '9')
off_sec = (10 * off_sec) + (*(value++) - '0');
/* Fractional part? */
if (*value == '.') {
value++;
while (*value >= '0' && *value <= '9') {
if (decimal > 0) {
off_nsec += decimal * (long)((*value) - '0');
decimal /= 10;
}
value++;
}
}
/* Skip trailing whitespace. */
while (*value == 't' || *value == 'n' || *value == 'v' ||
*value == 'f' || *value == 'r' || *value == ' ')
value++;
/* Garbage? */
if (*value)
break;
/* Negative? */
if (negative) {
off_sec = -off_sec;
off_nsec = -off_nsec;
}
/* Store. */
do {
__atomic_store_n(&offset_sec, off_sec, __ATOMIC_SEQ_CST);
__atomic_store_n(&offset_nsec, off_nsec, __ATOMIC_SEQ_CST);
} while (offset_sec != off_sec || offset_nsec != off_nsec);
} while (0);
errno = saved_errno;
}
每当加载库时,就会执行monotonic_init()
函数。(如果在LD_PRELOAD
中使用,则在main()
之前执行。(如果设置了LIBMONOTONIC_OFFSET
,则将其用作CLOCK_MONOTONIC的初始偏移。
对于测试(获取单调时钟(,这里有一个简单的测试程序示例,monoton.c:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
int main(void)
{
struct timespec now;
if (clock_gettime(CLOCK_MONOTONIC, &now) == -1) {
fprintf(stderr, "Cannot get monotonic clock: %s.n", strerror(errno));
return EXIT_FAILURE;
}
printf("%ld.%09ldn", (long)now.tv_sec, now.tv_nsec);
/* Set clock to zero. */
now.tv_sec = 0;
now.tv_nsec = 0;
if (clock_settime(CLOCK_MONOTONIC, &now) == -1) {
fprintf(stderr, "Cannot set monotonic clock: %s.n", strerror(errno));
return EXIT_FAILURE;
}
printf("Monotonic clock was set to %ld.%09ld successfully.n", (long)now.tv_sec, now.tv_nsec);
if (clock_gettime(CLOCK_MONOTONIC, &now) == -1) {
fprintf(stderr, "Cannot get monotonic clock: %s.n", strerror(errno));
return EXIT_FAILURE;
}
printf("%ld.%09ldn", (long)now.tv_sec, now.tv_nsec);
return EXIT_SUCCESS;
}
请注意,此程序没有以任何方式与libmononic.so链接。
最后,这里是Makefile来帮助编译所有这些:
CC := gcc
CFLAGS := -Wall -Wextra -O2
LDFLAGS := -ldl
TARGETS := libmonotonic.so monotonic
all: $(TARGETS)
clean:
rm -f *.o $(TARGETS)
%.o: %.c
$(CC) $(CFLAGS) -c $^
%.so: %.o
$(CC) $(CFLAGS) -shared -Wl,-soname,$@ $^ $(LDFLAGS) -o $@
monotonic: monotonic.o
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
请注意,由于此论坛使用Tabs,您需要在创建Makefile后,通过运行sed -e 's|^ *|t|' -i Makefile
来修复它的缩进。
运行make clean all
来构建库和示例程序。然后,运行例如
./monotonic
env LD_PRELOAD=$PWD/libmonotonic.so LIBMONOTONIC_OFFSET=-5321.2345 ./monotonic
./monotonic
在我的系统上,输出恰好是
1139233.519719376
Cannot set monotonic clock: Invalid argument.
1133912.791484465
Monotonic clock was set to 0.000000000 successfully.
0.000007698
1139234.531043242
Cannot set monotonic clock: Invalid argument.
请注意第一个数字和第二个数字之间的-5321差异:这是加载libmonetic.so时LIBMONOTONIC_OFFSET
环境变量的影响。
此外,当加载libmonetic.so时,clock_settime((调用成功。
请注意,如果二进制使用基于CLOCK_MONOTONIC的TIMER_ABSTIME计时器(通过timer_create()
、timer_settime()
等(,还应插入timer_create()
和timer_delete()
以记住哪些计时器基于CLOCK-MONOTONIC,以及timer_settime()
以在标志中设置TIMER_ABSTIME时调整初始时间。(timer_gettime()
总是返回相对值,因此不需要插入;相对计时器也不需要任何调整。(
检查std::chrono::steady_clock
- 在C++/Linux中设置单调时钟的一些技巧
- 从文本文件中读取时钟时间和事件时间并进行处理
- this_thread::sleep_for和计时时钟之间的关系是否由C++11标准指定
- 用时钟测量程序的运行时间
- 如何在C++中为高分辨率时钟声明变量?
- 如何在 C++17 中检索 std::filesystem::file_time_type 的时钟类型
- 我的时钟函数在 c++ 函数中不起作用
- 不同机器之间的标准时钟性能差异显著
- 计时库中的时钟和琐碎时钟概念
- 如何使用 CTIME/Chrono Libarey 将开始时间与系统时钟进行比较
- 如何在给定时钟偏移的情况下正确获取时间
- 使用时钟"storing"特定时间
- 时钟功能返回零时差
- 开发简单的时钟和日期类时遇到问题
- 标准::计时::时钟、硬件时钟和周期计数
- 查找给定序列是单调的还是严格单调的
- C++ 和 C# 中的等效系统时钟毫秒?
- time_t的时钟周期和获取时间问题
- 在没有单调时钟的系统上,是否不可能实现一致性
- 使用单调时钟的绝对时间