• linux 单调时间实现原理,Linux系统下的单调时间函数


    linux 单调时间实现原理,Linux系统下的单调时间函数_半夏256的博客-CSDN博客

    一、编写linux下应用程序的时候,有时候会用到高精度相对时间的概念,比如间隔100ms。那么应该使用哪个时间函数更准确呢?

    1、time

    该函数返回的是自1970年以来的秒数,显然精度不够,不能使用

    2、gettimeofday

    该函数返回的是自1970年以来的秒数和微秒数,精度显然是够了。我想有很多程序员也是用的这个函数来计算相对时间的,如果说系统时间因为ntp等原因发生时间跳变,

    么用这个函数来计算相对时间是不是就会出问题了。所以说这个函数也不能使用

    3、clock_gettime

    该函数提供了4种类型CLOCK_REALTIME、CLOCK_MONOTONIC、CLOCK_PROCESS_CPUTIMEID、CLOCK_THREAD_CPUTIME_ID。从字面意思可以判断出来,

    CLOCK_MONOTONIC提供了单调递增的时间戳,该函数返回值为自系统启动后秒数和纳秒数,但是该函数没有考虑ntp的情况,所以并不是绝对意义上的单调递增(见二)。

    CLOCK_REALTIME is affected by settime()/settimeofday() calls and can also be frequency corrected by NTP via adjtimex().

    CLOCK_MONOTONIC is not affected by settime()/settimeofday(), but is frequency adjusted by NTP via adjtimex().With Linux,NTP normally uses settimeofday() for large corrections (over half a second). The adjtimex() inteface allows for small clock frequency changes (slewing). This can be done in a few different ways, see the man page for adjtimex.

    CLOCK_MONOTONIC_RAW that will not be modified at all, and will have a linear correlation with the hardware counters.

    4、syscall(SYS_clock_gettime, CLOCK_MONOTONIC_RAW, &monotonic_time)

    该函数提供了真正意义上的单调递增时间(见三)

    二、glibc 中clock_gettime(CLOCK_MONOTONIC)的原理 查看glibc的代码可以看到这个数值是由内核计算的。 __vdso_clock_gettime-------->do_monotonic 这个函数的实现如下:

    notrace static noinline int do_monotonic(struct timespec *ts)

     1  {
     2 
     3 unsigned long seq, ns, secs;
     4 
     5 do {
     6 
     7 seq = read_seqbegin(>od->lock);
     8 
     9 secs = gtod->wall_time_sec;
    10 
    11 ns = gtod->wall_time_nsec + vgetns();
    12 
    13 secs += gtod->wall_to_monotonic.tv_sec;
    14 
    15 ns += gtod->wall_to_monotonic.tv_nsec;
    16 
    17 } while (unlikely(read_seqretry(>od->lock, seq)));
    18 
    19 
    20 
    21 /* wall_time_nsec, vgetns(), and wall_to_monotonic.tv_nsec
    22 
    23 * are all guaranteed to be nonnegative.
    24 
    25 */
    26 
    27 while (ns >= NSEC_PER_SEC) {
    28 
    29 ns -= NSEC_PER_SEC;
    30 
    31 ++secs;
    32 
    33 18. }
    34 
    35 19. ts->tv_sec = secs;
    36 
    37 20. ts->tv_nsec = ns;
    38 
    39 21.
    40 
    41 22. return 0;
    42 
    43 23. }

    这个代码读取墙上时间,然后加上相对于单调时间的便宜,从而得到单调时间,但是这里并没有考虑ntp通过adjtimex()调整小的时间偏差的情况,所以这个仍然不是绝对的单调递增

    三、内核clock_gettime系统调用

    在kernel/posix-timers.c中内核实现了clock_gettime的系统调用,包括CLOCK_REALTIME、CLOCK_MONOTONIC、CLOCK_MONOTONIC_RAW、CLOCK_REALTIME_COARSE、CLOCK_MONOTONIC_COARSE、CLOCK_BOOTTIME等类型,这里我们看一下CLOCK_MONOTONIC_RAW的实现

     1 struct k_clock clock_monotonic_raw = {
     2 
     3 clock_getres = hrtimer_get_res,
     4 
     5 clock_get = posix_get_monotonic_raw,
     6 
     7 };
     8 
     9 
    10 posix_timers_register_clock(CLOCK_MONOTONIC_RAW, &clock_monotonic_raw);
    11 
    12 
    13 /*
    14 
    15 * Get monotonic-raw time for posix timers
    16 
    17 */
    18 
    19 static int posix_get_monotonic_raw(clockid_t which_clock, struct timespec *tp)
    20 
    21 {
    22 
    23 getrawmonotonic(tp);
    24 
    25  return 0;
    26 
    27 }
    28 
    29 
    30 
    31 /**
    32 
    33 * getrawmonotonic - Returns the raw monotonic time in a timespec
    34 
    35  * @ts: pointer to the timespec to be set
    36 
    37  *
    38 
    39  * Returns the raw monotonic time (completely un-modified by ntp)
    40 
    41 */
    42 
    43 void getrawmonotonic(struct timespec *ts)
    44 
    45 {
    46 
    47 unsigned long seq;
    48 
    49  s64 nsecs;
    50 
    51 
    52 
    53 do {
    54 
    55 seq = read_seqbegin(&xtime_lock);
    56 
    57 nsecs = timekeeping_get_ns_raw();
    58 
    59 *ts = raw_time;
    60 
    61 
    62 
    63 } while (read_seqretry(&xtime_lock, seq));
    64 
    65 
    66  timespec_add_ns(ts, nsecs);
    67 
    68 }
    69 EXPORT_SYMBOL(getrawmonotonic);
    70 
    71 
    72 
    73 static inline s64 timekeeping_get_ns_raw(void)
    74 
    75 {
    76 
    77 cycle_t cycle_now, cycle_delta;
    78 
    79 struct clocksource *clock;
    80 
    81 /* read clocksource: */
    82 
    83 clock = timekeeper.clock;
    84 
    85 cycle_now = clock->read(clock);
    86 
    87 
    88 
    89 /* calculate the delta since the last update_wall_time: */
    90 
    91 cycle_delta = (cycle_now - clock->cycle_last) & clock->mask;
    92 
    93 
    94 
    95 /* return delta convert to nanoseconds using ntp adjusted mult. */
    96 
    97 return clocksource_cyc2ns(cycle_delta, clock->mult, clock->shift);
    98 }

    四、关于wall time和monotonic time

    wall time:xtime,取决于用于对xtime计时的clocksource,它的精度甚至可以达到纳秒级别,内核大部分时间都是使用xtime来获得当前时间信息,xtime记录的是自1970年当前时刻所经历的纳秒数。

    monotonic time: 该时间自系统开机后就一直单调地增加(ntp adjtimex会影响其单调性),它不像xtime可以因用户的调整时间而产生跳变,不过该时间不计算系统休眠的时间,也就是说,系统休眠时(total_sleep_time),monotoic时间不会递增。

    raw monotonic time: 该时间与monotonic时间类似,也是单调递增的时间,唯一的不同是,raw monotonic time不会受到NTP时间调整的影响,它代表着系统独立时钟硬件对时间的统计。

    boot time:与monotonic时间相同,不过会累加上系统休眠的时间(total_sleep_time),它代表着系统上电后的总时间。

    五、总结

    在linux下获取高精度单调递增的时间,只能使用syscall(SYS_clock_gettime, CLOCK_MONOTONIC_RAW, &monotonic_time)获取!

    在windows的多核环境下获取高精度时间是用:

    LARGE_INTEGER counter;

    QueryPerformanceCounter( &counter );

    return counter.QuadPart;

  • 相关阅读:
    常见二叉树问题
    leetcode上回溯法的使用
    搜索+回溯
    navicat连接登录windows10本地wsl的数据库
    皇后问题
    拓扑排序
    三路排序算法
    在 ServiceModel 客户端配置部分中,找不到引用协定“WebServiceTest.WebServiceSoap”的默认终结点元素。这可能是因为未找到应用程序的配置文件,或者是因为客户端元素
    WCF异常信息
    C# 制作ZIP压缩包
  • 原文地址:https://www.cnblogs.com/miaorn/p/16228823.html
Copyright © 2020-2023  润新知