Linux时间管理之hardware(1)(2)
PIT
PIT全称Programmable Interval Timer,是出现比较早的,比较菜的硬件。这种设备有8253/8254,对底层感兴趣的可以读drivers/clocksource/i8253.c,这种硬件的频率是1MHZ左右:
- #define PIT_TICK_RATE 1193182ul
PIT为啥没有落在available_clocksource中呢,因为后起之秀HPET的存在。Kernel中发现可以使用HPET,就不会用PIT作为始终源了。后面我们会分析到。
HPET
PIT 的精度较低,HPET 被设计来替代 PIT 提供高精度时钟中断至少 10MHz)。它是由微软和 Intel 联合开发的。一个 HPET 包括了一个固定频率的数值增加的计数器以及 3 到 32 个独立的计时器,这每一个计时器有包涵了一个比较器和一个寄存器保存一个数值,表示触发中断的时机)。每一个比较器都比较计数器中的数值和寄存器的数值,相等就会产生中断。
HPET这个时钟源的检测和注册是在前文提到的四大初始化中的最后一个:time_init
time_init
|________________x86_late_time_init |_________x86_init.timers.timer_init (arch/x86/kernel/x86_init.c) |________hpet_time_init |_____hpet_enable |____hpet_clocksource_register |_____set_default_time_irq |________________tsc_init |________x86_platform.calibrate_tsc (x86_init.c) |______native_calibrate_tsc |___quit_pit_calibrate |
在这个时候我们看到,time_init主要是两个部分,TSC是一个,HPETPIT)是一个。
- static struct irqaction irq0 = {
- .handler = timer_interrupt,
- .flags = IRQF_DISABLED | IRQF_NOBALANCING | IRQF_IRQPOLL | IRQF_TIMER,
- .name = "timer"
- };
- void __init setup_default_timer_irq(void)
- {
- setup_irq(0, &irq0);
- }
- /* Default timer init function */
- void __init hpet_time_init(void)
- {
- if (!hpet_enable())
- setup_pit_timer();
- setup_default_timer_irq();
- }
- static __init void x86_late_time_init(void)
- {
- x86_init.timers.timer_init();
- tsc_init(); //TSC part
- }
- /*
- * Initialize TSC and delay the periodic timer init to
- * late x86_late_time_init() so ioremap works.
- */
- void __init time_init(void)
- {
- late_time_init = x86_late_time_init;
- }
从上面加粗的部分可以看到,当HPET可以enable的时候,我们就不用PIT作为时钟源了,原因是HPET频率高,精度高。
从我的笔记本Linux的dmesg可看到:
[ 0.664201] hpet0: 8 comparators, 64-bit 14.318180 MHz counter |
我的笔记本的HPET,频率是14.318180MHz。
讲到这里,就不得不讲clocksource。Linux将真实的时钟做了抽象,用数据结构clocksource来管理这些硬件时钟源。
- /**
- * struct clocksource - hardware abstraction for a free running counter
- * Provides mostly state-free accessors to the underlying hardware.
- * This is the structure used for system time.
- *
- * @name: ptr to clocksource name
- * @list: list head for registration
- * @rating: rating value for selection (higher is better)
- * To avoid rating inflation the following
- * list should give you a guide as to how
- * to assign your clocksource a rating
- * 1-99: Unfit for real use
- * Only available for bootup and testing purposes.
- * 100-199: Base level usability.
- * Functional for real use, but not desired.
- * 200-299: Good.
- * A correct and usable clocksource.
- * 300-399: Desired.
- * A reasonably fast and accurate clocksource.
- * 400-499: Perfect
- * The ideal clocksource. A must-use where
- * available.
- * @read: returns a cycle value, passes clocksource as argument
- * @enable: optional function to enable the clocksource
- * @disable: optional function to disable the clocksource
- * @mask: bitmask for two's complement
- * subtraction of non 64 bit counters
- * @mult: cycle to nanosecond multiplier
- * @shift: cycle to nanosecond pisor (power of two)
- * @max_idle_ns: max idle time permitted by the clocksource (nsecs)
- * @maxadj: maximum adjustment value to mult (~11%)
- * @flags: flags describing special properties
- * @archdata: arch-specific data
- * @suspend: suspend function for the clocksource, if necessary
- * @resume: resume function for the clocksource, if necessary
- * @cycle_last: most recent cycle counter value seen by ::read()
- */
- struct clocksource {
- /*
- * Hotpath data, fits in a single cache line when the
- * clocksource itself is cacheline aligned.
- */
- cycle_t (*read)(struct clocksource *cs);
- cycle_t cycle_last;
- cycle_t mask;
- u32 mult;
- u32 shift;
- u64 max_idle_ns;
- u32 maxadj;
- #ifdef CONFIG_ARCH_CLOCKSOURCE_DATA
- struct arch_clocksource_data archdata;
- #endif
- const char *name;
- struct list_head list;
- int rating;
- int (*enable)(struct clocksource *cs);
- void (*disable)(struct clocksource *cs);
- unsigned long flags;
- void (*suspend)(struct clocksource *cs);
- void (*resume)(struct clocksource *cs);
- /* private: */
- #ifdef CONFIG_CLOCKSOURCE_WATCHDOG
- /* Watchdog related data, used by the framework */
- struct list_head wd_list;
- cycle_t cs_last;
- cycle_t wd_last;
- #endif
- } ____cacheline_aligned;
很重要的一个参数是rating时钟源分优劣,精度越高的时钟源的,rating值越大。从注释中我们可以看到:
- 1--99: 不适合于用作实际的时钟源,只用于启动过程或用于测试;
- 100--199:基本可用,可用作真实的时钟源,但不推荐;
- 200--299:精度较好,可用作真实的时钟源;
- 300--399:很好,精确的时钟源;
- 400--499:理想的时钟源,如有可能就必须选择它作为时钟源;
- static struct clocksource clocksource_hpet = {
- .name = "hpet",
- .rating = 250,
- .read = read_hpet,
- .mask = HPET_MASK,
- .flags = CLOCK_SOURCE_IS_CONTINUOUS,
- .resume = hpet_resume_counter,
- #ifdef CONFIG_X86_64
- .archdata = { .vclock_mode = VCLOCK_HPET },
- #endif
- };
从rating上HPET的rating是250,已经很不错了,为什么最终选择了TSC,available_clocksource中的acpi_pm又是什么?
评论暂时关闭