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