怎样获取系统时间

在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以下的精度会不够。

你可能感兴趣的:(timer,struct,IO,嵌入式,System,action)