Linux系統下的單調時間函數

1、編寫linux下應用程序的時候,有時候會用到高精度相對時間的概念,好比間隔100ms。那麼應該使用哪一個時間函數更準確呢?
    一、time
        該函數返回的是自1970年以來的秒數,顯然精度不夠,不能使用
    二、gettimeofday
        該函數返回的是自1970年以來的秒數和微秒數,精度顯然是夠了。我想有不少程序員也是用的這個函數來計算相對時間的,若是說系統時間由於ntp等緣由發生時間跳變,麼用這個函數來計算相對時間是否是就會出問題了。因此說這個函數也不能使用
    三、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.
linux

CLOCK_MONOTONIC_RAW that will not be modified at all, and will have a linear correlation with the hardware counters.
    四、syscall(SYS_clock_gettime, CLOCK_MONOTONIC_RAW, &monotonic_time)
        該函數提供了真正意義上的單調遞增時間(見三)程序員


2、glibc 中clock_gettime(CLOCK_MONOTONIC)的原理
    查看glibc的代碼能夠看到這個數值是由內核計算的。

    __vdso_clock_gettime-------->do_monotonic
    這個函數的實現以下:
    

點擊(此處)摺疊或打開函數

  1. notrace static noinline int do_monotonic(struct timespec *ts)
    spa

  2. {
    .net

  3.         unsigned long seq, ns, secs;
    orm

  4.         do {
    get

  5.                 seq = read_seqbegin(&gtod->lock);
    it

  6.                 secs = gtod->wall_time_sec;
    io

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

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

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

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


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

  12.          * are all guaranteed to be nonnegative.

  13.          */

  14.         while (ns >= NSEC_PER_SEC) {

  15.                 ns -= NSEC_PER_SEC;

  16.                 ++secs;

  17.         } 

  18.         ts->tv_sec = secs;

  19.         ts->tv_nsec = ns; 


  20.         return 0;

  21. }

這個代碼讀取牆上時間,而後加上相對於單調時間的便宜,從而獲得單調時間,可是這裏並無考慮ntp經過adjtimex()調整小的時間誤差的狀況,因此這個仍然不是絕對的單調遞增
3、內核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.                 .clock_getres = hrtimer_get_res,

  3.                 .clock_get = posix_get_monotonic_raw,

  4.         };



  5. posix_timers_register_clock(CLOCK_MONOTONIC_RAW, &clock_monotonic_raw);




  6. /*

  7.  * Get monotonic-raw time for posix timers

  8.  */

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

  10. {

  11.         getrawmonotonic(tp);

  12.         return 0;

  13. }



  14. /**

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

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

  17.  *

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

  19.  */

  20. void getrawmonotonic(struct timespec *ts)

  21. {

  22.         unsigned long seq;

  23.         s64 nsecs;


  24.         do {

  25.                 seq = read_seqbegin(&xtime_lock);

  26.                 nsecs = timekeeping_get_ns_raw();

  27.                 *ts = raw_time;


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


  29.         timespec_add_ns(ts, nsecs);

  30. }

  31. EXPORT_SYMBOL(getrawmonotonic);



  32. static inline s64 timekeeping_get_ns_raw(void)

  33. {

  34.         cycle_t cycle_now, cycle_delta;

  35.         struct clocksource *clock;


  36.         /* read clocksource: */

  37.         clock = timekeeper.clock;

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


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

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


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

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

  43. }


4、關於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),它表明着系統上電後的總時間。5、總結    在linux下獲取高精度單調遞增的時間,只能使用syscall(SYS_clock_gettime, CLOCK_MONOTONIC_RAW, &monotonic_time)獲取!

相關文章
相關標籤/搜索