how to caculate gettimeofday

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值