定时器: Linux中的定时器

1,014 阅读4分钟

在看 Node http 模块文档的时候, 才留意到 server.timeout 这个属性, 本想简单介绍一下, 但是在梳理过后发现关于 timeout 有庞大的内容支撑: server.timout -> node core timers -> uv timers -> linux msleep/hrtimer -> clocksource -> tsc -> cmos rtc -> clock cycle, 所以拆分成几部分大致做下介绍, 期望定时器系列结束之后, noder 能够大致明白: clock cycle 是如何驱动 linux 的 msleep/hrtimer; linux 的 timers 与 uv timers 的关系; node timers 与 uv timers的关系。

了解Linux的定时器的工作原理,有助于我们更深刻的认识Node中的定时器真面目。

定时器核心组件

  1. 石心 - 时钟源 - 硬件
  2. 守护者 - 记录时间 - 软件
  3. 定时狗 - 事件源 - 硬件

linux_timer.png

注意区分时刻和时间: 2020-11-22T12:31:37.696Z 时刻; 1000ms 是时间。系统初始化时刻来自RTC,后面的时刻都来自于 时刻 + 时间的计算

石心

硬件层面上有两个主要的模块: CMOS 和 CPU。 CMOS 可以在断电后继续振动更新RTC时间, CPU上电后在TSC寄存器可以在记录上电后的cycles总和,其本质只是一个收到高电压后+1的Counter

此时操作系统可以获取到: RTC, TSC, Frequecy,那么系统的时间就可以通过公式计算出来: RTC + TSC/Frequency

硬件的缺陷:

CMOS: RTC 是整个系统初始化的时间,但其本身精度较低,毫秒级,所以多台机器时钟很难在毫秒以下对齐

TSC: TSC 精度虽然可以在纳秒级别,但是只能记录上电之后的时间;TSC 通过64位存储,在10GHz情况下,溢出时间为2**64/(1e10*3600*24*365)≈58Y

注意区分时刻和时间: 2020-11-22T12:31:37.696Z 时刻; 1000ms 是时间。系统初始化时刻来自RTC,那么系统时刻 = 初始时刻 + 时间。

守护者

ClockSource是对硬件TSC的一种抽象,可以通过 read 方法读取到 TSC 寄存器中的 cycle,然后将cycle赋值给自身,只是在没有调用read方法之前,其时间落后于真正的TSC。

目前最高精度的硬件: tsc > hpet > acpi_pm, 他们都是时间记录者,里面存储的是cycle.

#$ cat /sys/devices/system/clocksource/clocksource0/available_clocksource 
tsc hpet acpi_pm 
#$ cat /sys/devices/system/clocksource/clocksource0/current_clocksource 
tsc

TimeKeeper 的主要工作就是主动调用ClockSource中的read,然后做cycle到timestamp的转换(时间到时刻的转换),并记录到自身。此时操作系统读取的时间就是从TimeKeeper中读取。

管家的缺陷:

TimeKeeper 只是时间的抽象,没有办法实现时间的自动更新,需要外部调用更新,所以时间落后于真正的TSC。

定时狗

无论是ClockSource还是TimerKeepr,他们只是对数字的一抽象,没有办法主动更新, 所以为了保持时间的实时性,需要一个程序不断的调用 read 来更新时间.

结合定时器: 石心中的内容,一个自更新的时间的原型大概是这样的:

while(true) {
 const cycles = TimeKeeper.ClockSOurce.read(tsc);
 const now = TimeKeeper.setTimer(cycles);
 if (now != '双11') {
 	schedule(); // 让CPU干点其他的,等会在更新时间,毕竟时间不能当饭吃
 }
}
优惠();

该定时器的问题是: 无法控制CPU回调的时间,可能超时;频繁CPU调度。

软件与硬件必须互相配合,上面的问题在没有硬件加持下是无法避免的,所以需要引入一个非常中的硬件APIC 高级可编程中断控制

目前最牛X的APIC为: lapic-deadline,而ClockeEventDevice就是对APIC硬件的抽象。

#$ cat /sys/devices/system/clockevents/clockevent0/current_device 
lapic-deadline

之所以说它最牛,是因为它本身就是一个(硬件)定时器: 可以给apic设定一个超时的cycle数量,当TSC中的cycle到达该值时,发送一个事件

有了基于cycle(精度)的事件(通知),实现高精度定时器就不在话下了;低精度定时器完全也可以通过高精度定时器来模拟出来。

总结

  1. CMOS中的RTC提供初始化时刻
  2. CMOS中的晶体提供震荡
  3. CPU中的TSC寄存器只记录上电以来的震荡个数cycle
  4. CPU中的APIC可以根据预设的cycle产生事件

硬件和软件相辅相成,需要效率支撑的通过硬件来支撑,硬件缺失的情况下可以通过软件来模拟。

关注我的微信公众号“SUNTOPO WLOG”,欢迎留言讨论,我会尽可能回复,感谢您的阅读。