天天看點

Linux系統下的單調時間函數

歡迎轉載,轉載請注明出處:http://forever.blog.chinaunix.net

一、編寫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)

{

        unsigned long seq, ns, secs;

        do {

                seq = read_seqbegin(&gtod->lock);

                secs = gtod->wall_time_sec;

                ns = gtod->wall_time_nsec + vgetns();

                secs += gtod->wall_to_monotonic.tv_sec;

                ns += gtod->wall_to_monotonic.tv_nsec;

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

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

         * are all guaranteed to be nonnegative.

         */

        while (ns >= NSEC_PER_SEC) {

                ns -= NSEC_PER_SEC;

                ++secs;

        } 

        ts->tv_sec = secs;

        ts->tv_nsec = ns; 

        return 0;

}

這個代碼讀取牆上時間,然後加上相對于單調時間的便宜,進而得到單調時間,但是這裡并沒有考慮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 = {

                .clock_getres = hrtimer_get_res,

                .clock_get = posix_get_monotonic_raw,

        };

posix_timers_register_clock(CLOCK_MONOTONIC_RAW, &clock_monotonic_raw);

/*

 * Get monotonic-raw time for posix timers

 */

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

        getrawmonotonic(tp);

/**

 * getrawmonotonic - Returns the raw monotonic time in a timespec

 * @ts: pointer to the timespec to be set

 *

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

void getrawmonotonic(struct timespec *ts)

        unsigned long seq;

        s64 nsecs;

                seq = read_seqbegin(&xtime_lock);

                nsecs = timekeeping_get_ns_raw();

                *ts = raw_time;

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

        timespec_add_ns(ts, nsecs);

EXPORT_SYMBOL(getrawmonotonic);

static inline s64 timekeeping_get_ns_raw(void)

        cycle_t cycle_now, cycle_delta;

        struct clocksource *clock;

        /* read clocksource: */

        clock = timekeeper.clock;

        cycle_now = clock->read(clock);

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

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

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

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

四、關于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)擷取!

繼續閱讀