高分辨率定时器和动态节拍设计说明¶
更多信息可以在 OLS 2006 会议的论文 “hrtimers and beyond” 中找到。 该论文是 OLS 2006 会议录第 1 卷的一部分,可以在 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 类型定时器的定时器轮相比,主要区别在于
按时间顺序排队到 rb-tree 中
独立于节拍(处理基于纳秒)
timeofday 和时钟源管理¶
John Stultz 的通用每日时间 (GTOD) 框架将大部分代码从特定于架构的区域移到通用管理框架中,如图 3(OLS 幻灯片第 18 页)所示。 特定于架构的部分减少到时钟源的低级硬件细节,这些时钟源在框架中注册,并根据质量进行选择。 低级代码提供硬件设置和读取例程,并初始化数据结构,通用时间保持代码使用这些数据结构将时钟节拍转换为基于纳秒的时间值。 所有其他与时间保持相关的功能都移到通用代码中。 GTOD 基本补丁已合并到 2.6.18 内核中。
有关通用每日时间框架的更多信息,请参见 OLS 2005 会议录第 1 卷
论文 “我们不再年轻:时间和定时器的新方法” 由 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 调用。 开销将远大于收益。 这就是为什么我们目前在 i386 SMP 系统上禁用高分辨率和动态节拍,这些系统会停止 C3 电源状态下的本地 APIC。 一种解决方法作为一种想法可用,但问题尚未得到解决。
定时器的按时间顺序插入提供了所有基础架构,以确定在添加定时器时是否必须重新编程事件设备。 该决定是按定时器库做出的,并在支持函数中跨每个 CPU 定时器库同步。 该设计允许系统为每个 CPU 定时器库利用单独的每个 CPU 时钟事件设备,但目前每个 CPU 仅利用一个可重新编程的时钟事件设备。
当定时器中断发生时,下一个事件中断处理程序从时钟事件分发代码中调用,并将过期的定时器从红黑树移动到单独的双向链表中,并调用 softirq 处理程序。 hrtimer 结构中的附加模式字段允许系统直接从下一个事件中断处理程序执行回调函数。 这仅限于可以在硬中断上下文中安全执行的代码。 例如,这适用于 nanosleep 使用的唤醒函数的常见情况。 在中断上下文中执行处理程序的优点是可以避免最多两次上下文切换 - 从中断的上下文到 softirq,以及到由过期定时器唤醒的任务。
一旦系统切换到高分辨率模式,周期性节拍就会关闭。 这将禁用每个系统全局周期性时钟事件设备 - 例如 i386 SMP 系统上的 PIT。
周期性节拍功能由每个 CPU hrtimer 提供。 回调函数在下一个事件中断上下文中执行,并更新 jiffies 并调用 update_process_times 和性能分析。 基于 hrtimer 的周期性节拍的实现旨在通过动态节拍功能进行扩展。 这允许使用单个时钟事件设备在 UP 系统上调度高分辨率定时器和周期性事件(jiffies 节拍、性能分析、进程记帐)。 这已被证明可以与 i386 上的 PIT 和 PPC 上的 Incrementer 一起使用。
用于运行 hrtimer 队列和执行回调的 softirq 已从与节拍绑定的定时器 softirq 中分离出来,以允许准确传递 itimer 和 POSIX 间隔定时器使用的高分辨率定时器信号。 此 softirq 的执行仍然可能被其他 softirq 延迟,但通过这种分离,整体延迟已得到显着改善。
图 5(OLS 幻灯片第 22 页)说明了转换。
动态节拍¶
动态节拍是基于 hrtimer 的周期性节拍替换(sched_tick)的逻辑结果。 sched_tick hrtimer 的功能由三个函数扩展
hrtimer_stop_sched_tick
hrtimer_restart_sched_tick
hrtimer_update_jiffies
当 CPU 进入空闲状态时,调用 hrtimer_stop_sched_tick()。 该代码评估下一个调度的定时器事件(来自 hrtimers 和定时器轮),并且如果下一个事件比下一个节拍更远,它会将 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