高分辨率定时器和动态节拍设计说明¶
更多信息可以在 OLS 2006 会议的论文 “hrtimers and beyond” 中找到。该论文是 OLS 2006 会议论文集第一卷的一部分,可以在 OLS 网站上找到:https://linuxkernel.org.cn/doc/ols/2006/ols2006v1-pages-333-346.pdf
该演讲的幻灯片可从以下网址获得:http://www.cs.columbia.edu/~nahum/w6998/papers/ols2006-hrtimers-slides.pdf
幻灯片包含五个图(第 2、15、18、20、22 页),说明了与时间相关的 Linux 子系统中的变化。图 #1(第 2 页)显示了在 hrtimers 和其他构建模块合并到主线之前 Linux 时间系统的设计。
注意:论文和幻灯片中讨论的是“时钟事件源”,而我们已经将其名称更改为“时钟事件设备”。
该设计包含以下基本构建模块
hrtimer 基础架构
timeofday 和时钟源管理
时钟事件管理
高分辨率定时器功能
动态节拍
hrtimer 基础架构¶
hrtimer 基础架构已合并到 2.6.16 内核中。基础实现的细节在 hrtimers - 高分辨率内核定时器的子系统 中介绍。另请参见图 #2 (OLS 幻灯片第 15 页)
与保存已启动的 timer_list 类型定时器的定时器轮的主要区别在于
按时间顺序排队到红黑树中
独立于节拍(处理基于纳秒)
timeofday 和时钟源管理¶
John Stultz 的通用时间 (GTOD) 框架将大部分代码从特定于架构的区域移到通用管理框架中,如图 #3 (OLS 幻灯片第 18 页) 所示。特定于架构的部分被简化为时钟源的底层硬件细节,这些时钟源在框架中注册并基于质量决策进行选择。底层代码提供硬件设置和读出例程,并初始化数据结构,这些数据结构由通用时间保持代码用于将时钟节拍转换为基于纳秒的时间值。所有其他与时间保持相关的功能都移到了通用代码中。GTOD 基础补丁已合并到 2.6.18 内核中。
有关通用时间框架的更多信息,请参见 OLS 2005 会议论文集第一卷
论文 “我们没有变得更年轻:一种新的时间和定时器方法” 由 J. Stultz、D.V. Hart 和 N. Aravamudan 撰写。
图 #3 (OLS 幻灯片第 18 页) 说明了这种转变。
时钟事件管理¶
虽然时钟源提供对单调递增时间值的读取访问,但时钟事件设备用于调度下一个事件中断。下一个事件目前被定义为周期性的,其周期在编译时定义。用于各种事件驱动功能的事件设备的设置和选择被硬连线到依赖于架构的代码中。这导致所有架构上的代码重复,并且使得更改系统配置以使用除已内置到架构中的事件中断设备之外的事件中断设备变得极其困难。当前设计的另一个含义是,为了提供诸如高分辨率定时器或动态节拍之类的新功能,有必要触及所有特定于架构的实现。
时钟事件子系统尝试通过提供一种通用解决方案来管理时钟事件设备及其在各种时钟事件驱动的内核功能中的使用来解决此问题。时钟事件子系统的目标是将时钟事件相关的架构依赖代码最小化为纯粹的硬件相关处理,并允许轻松添加和利用新的时钟事件设备。它还最大程度地减少了各个架构上的重复代码,因为它提供了通用的功能,一直到中断服务处理程序,该处理程序几乎固有地依赖于硬件。
时钟事件设备由依赖于架构的引导代码或在模块插入时注册。每个时钟事件设备都会使用特定于时钟的属性参数和回调函数填充数据结构。时钟事件管理通过使用指定的属性参数来决定时钟事件设备将用于支持的系统功能集。这包括区分每个 CPU 和每个系统的全局事件设备。
系统级全局事件设备用于 Linux 定期节拍。每个 CPU 事件设备用于提供本地 CPU 功能,例如进程记帐、分析和高分辨率定时器。
管理层将以下一个或多个功能分配给时钟事件设备
系统全局定期节拍(jiffies 更新)
cpu 本地 update_process_times
cpu 本地分析
cpu 本地下一个事件中断(非周期模式)
时钟事件设备将这些定时器中断相关功能的选择完全委托给管理层。时钟管理层将一个函数指针存储在设备描述结构中,该函数指针必须从硬件级处理程序调用。这从特定于架构的定时器中断处理程序中删除了很多重复的代码,并将对时钟事件设备的控制以及对核心代码的定时器中断相关功能的分配交给核心代码。
时钟事件层 API 非常小。除了时钟事件设备注册接口外,它还提供用于调度下一个事件中断、时钟事件设备通知服务以及支持挂起和恢复的函数。
该框架增加了大约 700 行代码,从而使内核二进制文件的大小增加了 2KB。i386 的转换删除了大约 100 行代码。二进制文件大小减少约 400 字节。我们认为,灵活性的提高和避免各个架构上的重复代码证明了二进制文件大小的略微增加是合理的。
架构的转换没有功能上的影响,但允许使用高分辨率和动态节拍功能,而无需更改时钟事件设备和定时器中断代码。转换完成后,只需将 kernel/time/Kconfig 文件添加到特定于架构的 Kconfig,并将动态节拍特定的调用添加到空闲例程(总共 3 行添加到空闲函数和 Kconfig 文件)即可启用高分辨率定时器和动态节拍
图 #4 (OLS 幻灯片第 20 页) 说明了这种转变。
高分辨率定时器功能¶
在系统引导期间,不可能使用高分辨率定时器功能,而使其成为可能将很困难且不会有任何有用的功能。必须完成时钟事件设备框架、时钟源框架 (GTOD) 和 hrtimers 本身的初始化,并且必须注册适当的时钟源和时钟事件设备,然后高分辨率功能才能工作。在初始化 hrtimers 之前,系统以通常的低分辨率定期模式工作。时钟源和时钟事件设备层提供通知功能,以通知 hrtimers 有新的硬件可用。hrtimers 会在切换到高分辨率模式之前验证注册的时钟源和时钟事件设备的可用性。这也确保了为高分辨率定时器配置的内核可以在缺少必要硬件支持的系统上运行。
高分辨率定时器代码不支持只有全局时钟事件设备的 SMP 机器。当发生中断时,支持此类硬件将涉及 IPI 调用。开销将远远大于收益。这就是为什么我们目前在 C3 电源状态下停止本地 APIC 的 i386 SMP 系统上禁用高分辨率和动态节拍的原因。一种解决方法作为一种想法是可用的,但是该问题尚未解决。
定时器的按时间顺序插入提供了所有基础结构,以确定在添加定时器时是否必须重新编程事件设备。该决定是每个定时器库做出的,并且在支持函数中跨每个 CPU 定时器库同步。该设计允许系统为每个 CPU 定时器库使用单独的每个 CPU 时钟事件设备,但是目前每个 CPU 仅使用一个可重新编程的时钟事件设备。
当定时器中断发生时,下一个事件中断处理程序从时钟事件分发代码中调用,并将过期的定时器从红黑树移动到单独的双向链表,并调用 softirq 处理程序。hrtimer 结构中的附加模式字段允许系统直接从下一个事件中断处理程序执行回调函数。这仅限于可以在硬中断上下文中安全执行的代码。例如,这适用于 nanosleep 使用的唤醒函数的常见情况。在中断上下文中执行处理程序的优点是可以避免最多两次上下文切换 — 从中断的上下文到 softirq,再到由过期的定时器唤醒的任务。
一旦系统切换到高分辨率模式,就会关闭定期节拍。这会禁用每个系统全局定期时钟事件设备 — 例如,i386 SMP 系统上的 PIT。
周期性嘀嗒功能由每个 CPU 的 hrtimer 提供。回调函数在下一个事件中断上下文中执行,并更新 jiffies,调用 update_process_times 和 profiling。基于 hrtimer 的周期性嘀嗒的实现旨在扩展动态嘀嗒功能。这允许在 UP 系统上使用单个时钟事件设备来调度高分辨率定时器和周期性事件(jiffies 嘀嗒、性能分析、进程记账)。这已被证明在 i386 上的 PIT 和 PPC 上的 Incrementer 上有效。
用于运行 hrtimer 队列和执行回调的 softirq 已与绑定到嘀嗒定时器的 softirq 分开,以允许精确传递 itimer 和 POSIX 间隔定时器使用的高分辨率定时器信号。此 softirq 的执行仍然可能被其他 softirq 延迟,但通过这种分离,整体延迟已显著改善。
图 #5 (OLS 幻灯片 p.22) 说明了这种转换。
动态嘀嗒¶
动态嘀嗒是基于 hrtimer 的周期性嘀嗒替换 (sched_tick) 的逻辑结果。sched_tick hrtimer 的功能通过三个函数进行扩展
hrtimer_stop_sched_tick
hrtimer_restart_sched_tick
hrtimer_update_jiffies
当 CPU 进入空闲状态时,调用 hrtimer_stop_sched_tick()。代码评估下一个调度的定时器事件(来自 hrtimer 和定时器轮),并且如果下一个事件比下一个嘀嗒更远,则它会将 sched_tick 重新编程到此未来事件,以允许更长的空闲睡眠,而不会被周期性嘀嗒无意义地中断。当空闲期间发生中断时也会调用此函数,但不会导致重新调度。之所以需要调用此函数,是因为中断处理程序可能已经激活了一个新的定时器,其过期时间早于上次调用 hrtimer_stop_sched_tick 时识别出的最近事件的时间。
当 CPU 在调用 schedule() 之前离开空闲状态时,调用 hrtimer_restart_sched_tick()。hrtimer_restart_sched_tick() 恢复周期性嘀嗒,该嘀嗒保持活动状态,直到下次调用 hrtimer_stop_sched_tick()。
当空闲期间发生中断时,从 irq_enter() 调用 hrtimer_update_jiffies(),以确保 jiffies 是最新的,并且中断处理程序不必处理可能过时的 jiffy 值。
动态嘀嗒功能提供统计值,这些值通过 /proc/stat 导出到用户空间,并且可以用于增强的电源管理控制。
该实现为进一步开发留有空间,例如完全无嘀嗒系统,其中时间片由调度程序控制,可变频率性能分析,以及未来完全删除 jiffies。
除了当前最初提交的 i386 支持之外,该补丁集已扩展到 x86_64 和 ARM。MIPS 和 PowerPC 的初始(正在进行中)支持也可用。
Thomas, Ingo