Linux time
linux对时间有两种需求:
第一就是获取当前时间,就像人想知道时间时看墙上挂的时钟一样,简称clock,如time()/ftime()/gettimeofday()/data()等这些系统调用,都是软件主动获取时间。linux称为clocksource,比如这台服务器就有三个clocksource,选择用一个就行:
[root@test] /home/huiwei$ cat /sys/devices/system/clocksource/clocksource0/available_clocksource
tsc hpet acpi_pm
[root@test] /home/huiwei$ cat /sys/devices/system/clocksource/clocksource0/current_clocksource
tsc
第二就是定时器(timer),就像闹钟一样,有点特殊的闹钟,这个闹钟不是说几点到了响铃通知我,而是说半个小时后响铃,或者我想睡觉8小时,8小时后就响铃,如timerfd_create()/sleep()/delay()这些函数。
timer分为硬件定时器和软件定时器,硬件定时器数量有限,软件定时器随便增加,软件定时器是由硬件定时器驱动起来的,CPU一直忙着干活,根据就不知道软件定时器是否超时了,只能靠硬件定时器周期性中断打断CPU,CPU再检查哪些软件定时器超时了,linux把硬件定时器称为clockevent。
[root@test] /home/huiwei$ cat /sys/devices/system/clockevents/broadcast/current_device
hpet 【广播timer】
[root@test] /home/huiwei$ cat /sys/devices/system/clockevents/clockevent0/current_device
lapic-deadline 【percpu timer】
clock和timer是概念,clocksource和clockevent是linux对这两个概念的抽象,体系结构无关的,hpet/tsc/pit/rtc/acpi_pm/lapic-deadline是硬件设备,是x86体系结构下的硬件设备,也许arm/ppc/mips下就是其它名称的硬件,硬件有的只提供clock功能,有的clock和timer功能都提供,clock和timer有全局的,也有局部的,如tsc就是一个cpu一个,hpet就是全局的,所有cpu都可以读。
看x86代码,linux初始化这些硬件设备,注册中断,中断处理中都调用到event_handler,简单理解为tick模块注册的函数,调用到tick的这个函数,它负责处理软件定时器,进行进程时间片计算等,当然tick觉得timer中断太多就可以把超时时间修改长一点,这样cpu不会被频繁打扰。
static void local_apic_timer_interrupt(void)
{
struct clock_event_device *evt = this_cpu_ptr(&lapic_events);
evt->event_handler(evt);
}
/*
* Local APIC timer interrupt. This is the most natural way for doing
* local interrupts, but local timer interrupts can be emulated by
* broadcast interrupts too. [in case the hw doesn't support APIC timers]
*
* [ if a single-CPU system runs an SMP kernel then we call the local
* interrupt as well. Thus we cannot inline the local irq ... ]
*/
DEFINE_IDTENTRY_SYSVEC(sysvec_apic_timer_interrupt)
{
local_apic_timer_interrupt();
}
/*
* Default timer interrupt handler for PIT/HPET
*/
static irqreturn_t timer_interrupt(int irq, void *dev_id)
{
global_clock_event->event_handler(global_clock_event);
return IRQ_HANDLED;
}
static void __init setup_default_timer_irq(void)
{
if (request_irq(0, timer_interrupt, flags, "timer", NULL))
pr_info("Failed to register legacy timer interrupt\n");
}