不受系统时钟影响的精确睡眠

发布时间 2023-10-18 14:20:16作者: azureology

需求

多线程编程需要控制某个线程的执行频率
采用系统内建的std::this_thread::sleep_for()函数会受到系统时钟跳变的影响
希望使用一个单调递增的时钟作为参考进行休眠。

实现

截取一段man clock_gettime的原文:

       CLOCK_REALTIME
              System-wide  clock  that  measures real (i.e., wall-clock) time.  Setting this clock requires appropriate privileges.  This clock
 is affected by discontinuous jumps in the system time (e.g., if the system administrator manually changes
              the clock), and by the incremental adjustments performed by adjtime(3) and NTP.

       CLOCK_REALTIME_COARSE (since Linux 2.6.32; Linux-specific)
              A faster but less precise version of CLOCK_REALTIME.  Use when you need very fast, but not fine-grained timestamps.  Requires per
-architecture support, and probably also architecture support for this flag in the vdso(7).

       CLOCK_MONOTONIC
              Clock that cannot be set and represents monotonic time since some unspecified starting point.  This clock is not affected by discontinuous jumps in the system time (e.g., if the system administrator manually changes the clock), but  is
              affected by the incremental adjustments performed by adjtime(3) and NTP.

再看看内核代码hrtimer.c指出select在内核中使用的CLOCK_MONOTONIC是单调递增的(见参考文献2),即它的休眠不受时间同步影响。

int __sched schedule_hrtimeout_range(ktime_t *expires, u64 delta,
				     const enum hrtimer_mode mode)
{
	return schedule_hrtimeout_range_clock(expires, delta, mode,
					      CLOCK_MONOTONIC);
}

根据以上结论,我们可以利用<sys/time.h>提供的select方法完成精确睡眠,实现参考GoogleGroups代码。

#include <sys/time.h>

void sleep(long int ms)
{
    struct timeval delay;
    delay.tv_sec = static_cast<long int>(ms / 1000);
    delay.tv_usec = static_cast<long int>((ms - delay.tv_sec * 1000) * 1000);
    (void) select(0, NULL, NULL, NULL, &delay);
}

参考

clock_gettime(3): clock/time functions - Linux man page
linux/kernel/time/hrtimer.c at master · torvalds/linux
using select as a sleep call