- 论坛徽章:
- 0
|
(continued)
Now Let's look back our deduction. We get the correct time if we get the right last_time. If the last_time is incorrect, how can we get the real world time?
The last_time is based on timer interrupt. The timer, which is as a primitive source to drive the whole system, is architecture-specific. Usually it is located on PIT of the spcific architecture. In our example it is AT91SAM9263's PIT.
The procedure to calculate the last time is as follows:
1. Registration
a. Setting the PIT with HZ
b. Enalbling the PIT
2. After enabling PIT, it increases automatically. When the corresponding register
overflows, it generates an interrupt, which is captured by CPU. And then CPU
dispatches it to the corresponding interrupt routine. This is the
timer_interrupt, whichi is registered in arch/arm/mach-at91/at91sam926x_time.c.
Now lets look at the codes to find how it is handled.
a. at91sam926x_timer_interrupt is called
...
write_seqlock(&xtime_lock);
nr_ticks = PITCNT(at91_sys_read(AT91_PIT_PIVR));
do {
timer_tick();
nr_ticks --;
} while (nr_ticks);
write_sequnlock(&xtime_unlock);
...
/* arch/arm/kernel/time.c */
void timer_tick(void)
{
...
do_timer(1);
#ifndef CONFIG_SMP
update_process_times(user_mode(get_irq_regs()));
#endif
...
}
b. do_timer() and update_process_times() are considered here. These two
functions are in kernel/timer.c
void do_timer(unsigned long ticks)
{
jiffies_64 += ticks;
#ifdef CONFIG_GENERIC_TIME
clocksource_accumulate(clock, clocksource_read(clock));
#endif
}
This function just increass the jiffes_64. Function gettimeofday() uses
xtime. So xtime must be increased in update_process_times(). Let's look at
update_process_times().
void update_process_times(int user_tick)
{
...
run_local_timers();
...
}
void run_local_timers(void)
{
raise_softirq(TIMER_SOFTIRQ);
softlockup_tick();
}
c. The kernel handles the update_time in SOFTIRQ handler. This slows down the
wall time in a specific moment, especially when system's load is very high.
static void run_timer_softirq(struct softirq_action *action)
{
...
update_times();
...
}
Solution
we can move update_times() from softirq handler to interrupt handler and update the wall time in do_timer(). Since the timer_interrupt is set to IRQF_DISABLED the process handling is atomic. see code in arch/arm/mach-at91/at91sam926x_timer.c for definition of irq flags.
void do_timer(unsigned long ticks)
{
jiffies_64 += ticks;
update_times(); //move from softirq handler
#ifdef CONFIG_GENERIC_TIME
clocksource_accumulate(clock, clocksource_read(clock));
#endif
}
Conclusion:
gettimeofday() returns the expected real world time.
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/103132/showart_2024003.html |
|