现在的位置: 首页 > 综合 > 正文

怎样获取系统时间

2013年09月19日 ⁄ 综合 ⁄ 共 4455字 ⁄ 字号 评论关闭

在mobile linux中的gettimeofday,按照user layer的定义,是micro second(us)的精度。 但是真的精确么?


gettimeofday --> sys_gettimeofday -->do_gettimeofday
void do_gettimeofday(struct timeval *tv)
{
    unsigned long flags;
    unsigned long seq;
    unsigned long usec, sec;

    do {
        seq = read_seqbegin_irqsave(&xtime_lock,
flags);

        usec = system_timer->offset();
        sec = xtime.tv_sec;
        usec += xtime.tv_nsec
/ 1000;

    } while (read_seqretry_irqrestore(&xtime_lock,
seq, flags));


    /* usec may have gone up a lot: be safe */
    while (usec >= 1000000) {
        usec -= 1000000;
        sec++;
    }

    tv->tv_sec = sec;
    tv->tv_usec = usec;
}
由上函数可见:
usec = system_timer->offset();
usec += xtime.tv_nsec
/ 1000;

是关键。
offset 是在time_init中赋值的:
void __init time_init(void)
{
#ifndef CONFIG_GENERIC_TIME
    if (system_timer->offset == NULL)
        system_timer->offset = dummy_gettimeoffset;
#endif
    system_timer->init();

#ifdef CONFIG_NO_IDLE_HZ
    if (system_timer->dyn_tick)
        system_timer->dyn_tick->lock = SPIN_LOCK_UNLOCKED;
#endif
}
CONFIG_GENERIC_TIME 是定义了的,而且我们的system_timer->offset为空,system_timer是一个全局变量,
是在arch/arm/atxx.c中赋值:
MACHINE_START(INTEGRATOR, "ARM")
    .phys_io    = UART0_BASE,
    .io_pg_offst    = ((IO_ADDRESS(UART0_BASE)) >> 18) & 0xfffc,
    .boot_params    = PHYS_OFFSET + 0x00000100,
    .map_io        = ap_map_io,
    .init_irq    = ap_init_irq,
    .timer        = &ap_timer,
    .init_machine    = ap_init,
MACHINE_END
所以会用dummy_gettimeoffset,而这个函数是一个空函数;
现在来看xtime的nano
second是如何算出来的。

xtime的初始化在timekeeping_init
void __init timekeeping_init(void)
{
 ......
     clock = clocksource_get_next();
    clocksource_calculate_interval(clock, NTP_INTERVAL_LENGTH);
    clock->cycle_last = clocksource_read(clock);

    xtime.tv_sec
= sec;

    xtime.tv_nsec
= 0;


 ......
}

clock = clocksource_get_next();会选择一个clock rate最高的clock source,在嵌入式环境中一般就只有一个
PIT(program interrupt timer),所以只会是这个。
xtime.tv_nsec = 0;
初始值是0;

那这个值是什么时候update的呢?
在 start_kernel中的init_timers中,会注册叫 run_timer_softirq的softirq,来看一下这个函数:
/*
 * This function runs timers and the timer-tq in bottom half context.
 */
static void run_timer_softirq(struct softirq_action *h)
{
    tvec_base_t *base = per_cpu(tvec_bases, raw_smp_processor_id());

    update_times();
    MARK(kernel_timer_update_time,
    "%8b %*.*r %*.*r",
        jiffies_64,
        sizeof(xtime),
__alignof__(
xtime),
&
xtime,
        sizeof(wall_to_monotonic),
__alignof__(
wall_to_monotonic),
        &wall_to_monotonic);
    hrtimer_run_queues();

    if (time_after_eq(jiffies, base->timer_jiffies))
        __run_timers(base);
}
然后是update_times-->update_wall_time

/**
 * update_wall_time - Uses the current clocksource to increment the wall time
 *
 * Called from the timer interrupt, must hold a write on xtime_lock.
 */
static void update_wall_time(void)
{
    cycle_t cycle_now;

    /* Make sure we're fully resumed: */
    if (unlikely(timekeeping_suspended))
        return;

#ifdef CONFIG_GENERIC_TIME
    cycle_now = clocksource_read(clock);  //read back jiffies
#else
    cycle_now = clock->cycle_last + clock->cycle_interval;
#endif
    clocksource_accumulate(clock, cycle_now);

    clock->xtime_nsec
+= (s64)
xtime.tv_nsec
<< clock->shift;


    /* normally this loop will run just once, however in the
     * case of lost or late ticks, it will accumulate correctly.
     */
    while (clock->cycle_accumulated >= clock->cycle_interval) {
        /* accumulate one interval */
        clock->xtime_nsec
+= clock->
xtime_interval;
        clock->cycle_accumulated -= clock->cycle_interval;

        if (clock->xtime_nsec
>= (u64)NSEC_PER_SEC << clock->shift) {

            clock->xtime_nsec
-= (u64)NSEC_PER_SEC << clock->shift;

            xtime.tv_sec++;
            second_overflow();
        }

        /* interpolator bits */
        time_interpolator_update(clock->xtime_interval
                        >> clock->shift);

        /* accumulate error between NTP and clock interval */
        clock->error += current_tick_length();
        clock->error -= clock->xtime_interval
<< (TICK_LENGTH_SHIFT - clock->shift);

    }

    /* correct the clock when NTP error is too big */
    clocksource_adjust(clock->cycle_accumulated);

    /* store full nanoseconds into xtime */
    xtime.tv_nsec
= (s64)clock->
xtime_nsec
>> clock->shift;

    clock->xtime_nsec
-= (s64)
xtime.tv_nsec
<< clock->shift;


    /* check to see if there is a new clocksource to use */
    change_clocksource();
    update_vsyscall(&xtime,
clock);

}
cycle_now = clocksource_read(clock); 这个clock就是timekeeping_init中,用clocksource_get_next选择的
clock,在我们的例子里是:
#define JIFFIES_SHIFT 8
struct clocksource clocksource_jiffies = {
    .name        = "jiffies",
    .rating        = 1, /* lowest valid rating*/
    .read        = jiffies_read,
    .mask        = 0xffffffff, /*32bits*/
    .mult        = NSEC_PER_JIFFY << JIFFIES_SHIFT, /* details above */
    .shift        = JIFFIES_SHIFT,
};
cycle_now 取得是就是jiffies,他的基准值是jiffies,ns是一个估计出来的值。

结论:用gettimeofday计算jiffies以上的值是可以的,但是jiffies以下的精度会不够。

抱歉!评论已关闭.