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



一、编写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)

2. {

3. unsigned long seq, ns, secs;

4. do {

5. seq = read_seqbegin(>od->lock);

6. secs = gtod->wall_time_sec;

7. ns = gtod->wall_time_nsec + vgetns();

8. secs += gtod->wall_to_monotonic.tv_sec;

9. ns += gtod->wall_to_monotonic.tv_nsec;

10. } while (unlikely(read_seqretry(>od->lock, seq)));

11.

12. /* wall_time_nsec, vgetns(), and wall_to_monotonic.tv_nsec

13. * are all guaranteed to be nonnegative.

14. */

15. while (ns >= NSEC_PER_SEC) {

16. ns -= NSEC_PER_SEC;

17. ++secs;

18. }

19. ts->tv_sec = secs;

20. ts->tv_nsec = ns;

21.

22. return 0;

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的实现

struct k_clock clock_monotonic_raw = {

2. .clock_getres = hrtimer_get_res,

3. .clock_get = posix_get_monotonic_raw,

4. };

5.

6.

7. posix_timers_register_clock(CLOCK_MONOTONIC_RAW, &clock_monotonic_raw);

8.

9.

10.

11. /*

12. * Get monotonic-raw time for posix timers

13. */

14. static int posix_get_monotonic_raw(clockid_t which_clock, struct timespec *tp)

15. {

16. getrawmonotonic(tp);

17. return 0;

18. }

19.

20.

21. /**

22. * getrawmonotonic - Returns the raw monotonic time in a timespec

23. * @ts: pointer to the timespec to be set

24. *

25. * Returns the raw monotonic time (completely un-modified by ntp)

26. */

27. void getrawmonotonic(struct timespec *ts)

28. {

29. unsigned long seq;

30. s64 nsecs;

31.

32. do {

33. seq = read_seqbegin(&xtime_lock);

34. nsecs = timekeeping_get_ns_raw();

35. *ts = raw_time;

36.

37. } while (read_seqretry(&xtime_lock, seq));

38.

39. timespec_add_ns(ts, nsecs);

40. }

41. EXPORT_SYMBOL(getrawmonotonic);

42.

43.

44. static inline s64 timekeeping_get_ns_raw(void)

45. {

46. cycle_t cycle_now, cycle_delta;

47. struct clocksource *clock;

48.

49. /* read clocksource: */

50. clock = timekeeper.clock;

51. cycle_now = clock->read(clock);

52.

53. /* calculate the delta since the last update_wall_time: */

54. cycle_delta = (cycle_now - clock->cycle_last) & clock->mask;

55.

56. /* return delta convert to nanoseconds using ntp adjusted mult. */

57. return clocksource_cyc2ns(cycle_delta, clock->mult, clock->shift);

58. }

四、关于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;

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值