6.2 The Linux Timekeeping Architecture | |||||||||
· The kernel uses two basic timekeeping functions: | |||||||||
one to keep the current time up-to-date and | |||||||||
another to count the number of nanoseconds that have elapsed within the current second | |||||||||
Data Structures of the Timekeeping Architecture | |||||||||
· include/asm-i386/timer.h | |||||||||
struct timer_opts { | |||||||||
char* name; | |||||||||
void (*mark_offset)(void); | |||||||||
unsigned long (*get_offset)(void); | |||||||||
unsigned long long (*monotonic_clock)(void); | |||||||||
void (*delay)(unsigned long); | |||||||||
}; | |||||||||
struct init_timer_opts { | |||||||||
int (*init)(char *override); | |||||||||
struct timer_opts *opts; | |||||||||
}; | |||||||||
/* list of timers, ordered by preference, NULL terminated */ | |||||||||
static struct init_timer_opts* __initdata timers[] = { | |||||||||
#ifdef CONFIG_X86_CYCLONE_TIMER | |||||||||
&timer_cyclone_init, | |||||||||
#endif | |||||||||
#ifdef CONFIG_HPET_TIMER | |||||||||
&timer_hpet_init, | |||||||||
#endif | |||||||||
#ifdef CONFIG_X86_PM_TIMER | |||||||||
&timer_pmtmr_init, | |||||||||
#endif | |||||||||
&timer_tsc_init, | |||||||||
&timer_pit_init, | |||||||||
NULL, | |||||||||
}; | |||||||||
/* iterates through the list of timers, returning the first | |||||||||
* one that initializes successfully. | |||||||||
*/ | |||||||||
struct timer_opts* __init select_timer(void) | |||||||||
{ | |||||||||
int i = 0; | |||||||||
/* find most preferred working timer */ | |||||||||
while (timers[i]) { | |||||||||
if (timers[i]->init) | |||||||||
if (timers[i]->init(clock_override) == 0) | |||||||||
return timers[i]->opts; | |||||||||
++i; | |||||||||
} | |||||||||
panic("select_timer: Cannot find a suitable timer/n"); | |||||||||
return NULL; | |||||||||
} | |||||||||
u64 get_jiffies_64(void) | |||||||||
{ | |||||||||
unsigned long seq; | |||||||||
u64 ret; | |||||||||
do { | |||||||||
seq = read_seqbegin(&xtime_lock); | |||||||||
ret = jiffies_64; | |||||||||
} while (read_seqretry(&xtime_lock, seq)); | |||||||||
return ret; | |||||||||
} | |||||||||
struct timespec { | |||||||||
time_t | tv_sec; | /* seconds */ | |||||||
long | tv_nsec; | /* nanoseconds */ | |||||||
}; | |||||||||
· Timekeeping Architecture in Uniprocessor Systems | |||||||||
· Initialization phase | |||||||||
void __init time_init(void) | |||||||||
{ | |||||||||
#ifdef CONFIG_HPET_TIMER | |||||||||
if (is_hpet_capable()) { | |||||||||
/* | |||||||||
* HPET initialization needs to do memory-mapped io. So, let | |||||||||
* us do a late initialization after mem_init(). | |||||||||
*/ | |||||||||
late_time_init = hpet_time_init; | |||||||||
return; | |||||||||
} | |||||||||
#endif | |||||||||
xtime.tv_sec = get_cmos_time(); | |||||||||
xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); | |||||||||
set_normalized_timespec(&wall_to_monotonic, | |||||||||
-xtime.tv_sec, -xtime.tv_nsec); | |||||||||
cur_timer = select_timer(); | |||||||||
printk(KERN_INFO "Using %s for high-res timesource/n",cur_timer->name); | |||||||||
time_init_hook(); | |||||||||
} | |||||||||
1. Initializes the xtime variable | |||||||||
2. Initializes the wall_to_monotonic variable | |||||||||
3. If the kernel supports HPET, it invokes the hpet_enable( ) function to determine | |||||||||
whether the ACPI firmware has probed the chip and mapped | |||||||||
its registers in the memory address space | |||||||||
※hpet_enable( ) programs the first timer of the HPET chip so that it raises the | |||||||||
IRQ 0 interrupt 1000 times per second. Otherwise, if the HPET chip is not available, | |||||||||
the kernel will use the PIT: the chip has already been programmed by the init_IRQ( ) | |||||||||
function to raise 1000 timer interrupts per second | |||||||||
4. Invokes select_timer( ) to select the best timer source available in the system, | |||||||||
and sets the cur_timer variable to the address of the corresponding timer object | |||||||||
5. Invokes setup_irq( 0,&irq0) to set up the interrupt gate corresponding to IRQ0 | |||||||||
the line associated with the system timer interrupt source (PIT or HPET) | |||||||||
static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, CPU_MASK_NONE, "timer", NULL, NULL}; | |||||||||
· The timer interrupt handler | |||||||||
The timer_interrupt( ) function is the interrupt service routine (ISR) | |||||||||
of the PIT or of the HPET; it performs the following steps: | |||||||||
1. Protects the time-related kernel variables | |||||||||
by issuing a write_seqlock() on the xtime_lock seqlock | |||||||||
2. Executes the mark_offset method of the cur_timer timer object | |||||||||
3. Invokes the do_timer_interrupt( ) function | |||||||||
which in turn performs the following actions: | |||||||||
a. Increases by one the value of jiffies_64 | |||||||||
b. Invokes the update_times( ) function to update | |||||||||
the system date and time and to compute the current system load | |||||||||
c. Invokes the update_process_times( ) function to perform several | |||||||||
time-related accounting operations for the local CPU | |||||||||
d. Invokes the profile_tick( ) function | |||||||||
e. If the system clock is synchronized with an external clock | |||||||||
invokes the set_rtc_mmss( ) function once every 660 seconds | |||||||||
4. Releases the xtime_lock seqlock by invoking write_sequnlock(). | |||||||||
5. Returns the value 1 to notify that the interrupt has been effectively handled | |||||||||
Timekeeping Architecture in Multiprocessor Systems | |||||||||
Passed |
Understanding the linux kernel-ch6-Timing Measurements
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.