Linux timer-1-内核文档翻译
一、hrtimers.rst
注:翻译自 msm-5.4/Documentation/timers/hrtimers.rst
============================================================
hrtimers - 高分辨率内核计时器子系统
============================================================
此补丁引入了一个新的高分辨率内核计时器子系统。
有人可能会问:我们已经有一个计时器子系统 (kernel/timers.c),为什么还需要两个计时器子系统?经过反复尝试将高分辨率和高精度功能集成到现有的计时器框架中,并在实践中测试了各种类似的高分辨率计时器实现后,我们得出结论,计时器轮盘(timer wheel)代码从根本上来说并不适合这种方法。我们最初并不相信这一点(“一定有办法解决这个问题”),并花费了相当大的精力尝试将各种功能集成到计时器轮盘中,但最终失败了。事后看来,这种集成之所以困难/不可能,有以下几个原因:
- 强制以相同的方式处理低分辨率和高分辨率计时器会导致很多妥协、宏和 #ifdef 混乱。timers.c 代码非常“紧密地”围绕 jiffies 和 32 位假设进行编码,并且多年来一直针对相对较窄的用例(相对较窄的 HZ 范围内的 jiffies)进行打磨和微优化——因此,即使是对其进行微小的扩展也很容易破坏轮子概念,导致更严重的妥协。计时器轮子代码非常好且紧凑,在当前使用中没有任何问题——但它根本不适合扩展用于高分辨率计时器。
- 级联不可预测的 [O(N)] 开销会导致延迟,这需要更复杂的高精度定时器处理,从而降低鲁棒性。这样的设计仍然会导致相当大的计时误差。级联是定时器轮盘概念的一个基本属性,如果要“设计出”它,必然会以不可接受的方式降低 timers.c 代码其他部分的性能。
- 在定时器轮盘之上实现的当前 posix-timer 子系统已经引入了相当复杂的处理,需要在 settimeofday 或 NTP 时间重新调整绝对 CLOCK_REALTIME 定时器 - 进一步通过示例说明了我们的经验:定时器轮盘数据结构对于高分辨率定时器来说过于僵化。
- 定时器轮盘代码最适合那些可以识别为“超时”的用例。此类超时通常用于覆盖各种 I/O 路径中的错误情况,例如网络和块 I/O。绝大多数此类定时器永不过期,并且很少重新级联,因为预期的正确事件会及时到达,因此它们可以在需要进一步处理之前从定时器轮盘中移除。因此,这些超时的用户可以接受定时器轮盘在粒度和精度方面的权衡,并且在很大程度上期望定时器子系统的开销接近于零。对于它们来说,精确计时并非核心目的——事实上,大多数使用的超时值都是临时的。对于它们来说,保证实际超时完成的处理至多是必要之恶(因为大多数超时在完成之前就被删除了),因此这应该尽可能地廉价且不具侵入性。
精确计时器的主要用户是使用 nanosleep、posix-timers 和 itimer 接口的用户空间应用程序。此外,内核用户(例如需要精确定时事件(例如多媒体)的驱动程序和子系统)也可以受益于单独的高精度计时器子系统的可用性。
虽然该子系统目前尚不提供高精度时钟源,但 hrtimer 子系统可以轻松扩展高精度时钟功能,并且相关补丁已经存在且正在快速完善。实时和多媒体应用的需求日益增长,以及其他潜在用户对精确计时器的需求,也为区分“超时”子系统和“精确计时器”子系统提供了另一个理由。
另一个潜在的好处是,这种分离允许对现有的计时器轮进行更多针对低分辨率和低精度用例的专用优化——一旦精度敏感的 API 从计时器轮中分离出来并迁移到 hrtimers。例如,我们可以将超时子系统的频率从 250 Hz 降低到 100 HZ(甚至更低)。
hrtimer 子系统实现细节
----------------------------------------
基本设计考虑如下:
- 简洁性
- 数据结构不受 jiffies 或任何其他粒度的限制。所有内核逻辑均以 64 位纳秒分辨率运行,不做任何妥协。
- 简化现有的与时间相关的内核代码
另一个基本要求是在激活时立即将定时器入队并排序。在研究了基数树和哈希等几种可能的解决方案后,我们选择了红黑树作为基本数据结构。红黑树在内核中以库的形式提供,并用于各种性能关键领域,例如内存管理和文件系统。红黑树仅用于按时间排序,而使用单独的列表使到期代码能够快速访问排队的定时器,而无需遍历红黑树。
(这个单独的列表对于我们稍后引入高分辨率时钟也很有用,我们需要单独的待处理和过期队列,同时保持时间顺序不变。)
然而,按时间顺序入队并非纯粹为了实现高分辨率时钟,它还能简化基于低分辨率 CLOCK_REALTIME 的绝对计时器的处理。现有实现需要维护一个额外的列表,其中包含所有已启用的绝对 CLOCK_REALTIME 计时器,并进行复杂的锁定。对于 settimeofday 和 NTP 的情况,所有计时器(!)都必须出队,时间更改代码必须逐个修复它们,然后所有计时器都必须重新入队。按时间顺序入队并以绝对时间单位存储到期时间,从 posix-timer 实现中移除了所有这些复杂且扩展性较差的代码——无需修改红黑树即可轻松设置时钟。这也使得 posix-timer 的处理总体上更加简单。
hrtimer 的锁定和每个 CPU 的行为主要借鉴了现有的 timer wheel 代码,因为它已经成熟且非常适合。由于数据结构不同,共享代码实际上并不是什么好事。此外,hrtimer 函数现在具有更清晰的行为和更清晰的名称 - 例如 hrtimer_try_to_cancel() 和 hrtimer_cancel() [它们大致相当于 del_timer() 和 del_timer_sync()] - 因此它们之间在算法层面上不再存在直接的 1:1 映射,因此也不再具有真正的代码共享潜力。
基本数据类型:所有时间值,无论是绝对值还是相对值,都采用一种特殊的纳秒精度类型:ktime_t. ktime_t 值和操作的内核内部表示通过宏和内联函数实现,并且可以在“混合联合”类型和纯“标量”64 位纳秒表示(编译时)之间切换。混合联合类型优化了 32 位 CPU 上的时间转换。这种可在编译时选择的 ktime_t 存储格式的实现是为了避免 64 位乘法和除法对 32 位 CPU 的性能影响。此类操作通常需要在内核和用户空间接口提供的存储格式与内部时间格式之间进行转换。(更多详情,请参阅 include/linux/ktime.h 文件。)
hrtimers - 定时器值的舍入
----------------------------------
hrtimer 代码会将定时器事件舍入到较低分辨率的时钟,因为这是必须的。否则,它根本不会进行任何人工舍入。
一个问题是,clock_getres() 接口应该返回给用户的分辨率值是什么。这将返回给定时钟的实际分辨率——无论是低分辨率、高分辨率还是人为设置的低分辨率。
hrtimers - 测试与验证
----------------------------------
我们在 hrtimers 之上使用了高精度时钟子系统,在实践中验证了 hrtimer 的实现细节,并运行了 posix 定时器测试,以确保符合规范。我们还对低精度时钟进行了测试。
hrtimer 补丁将以下内核功能转换为使用 hrtimers:
- nanosleep
- itimers
- posix-timers
nanosleep 和 posix-timers 的转换使得 nanosleep 和 clock_nanosleep 得以统一。
代码已成功编译到以下平台:
i386、x86_64、ARM、PPC、PPC64、IA64
代码已在以下平台上运行测试:
i386(UP/SMP)、x86_64(UP/SMP)、ARM、PPC
hrtimers 也已集成到 -rt 树中,并附带一个基于 hrtimers 的高精度时钟实现,因此 hrtimers 代码得到了大量的测试和实践应用。
Thomas Gleixner、Ingo Molnar
二、highres.rst
注:翻译自 msm-5.4/Documentation/timers/highres.rst
============================================================
高分辨率计时器和动态滴答设计笔记
===============================================================
更多信息请参阅 OLS 2006 演讲论文“hrtimers and beyond”。该论文是 OLS 2006 会议论文集第 1 卷的一部分,可在 OLS 网站上找到:https://www.kernel.org/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 中与 time(r) 相关的子系统的变化。图 1(第 2 页)展示了在 hrtimer 和其他构建块合并到主线之前,Linux time(r) 系统的设计。
注意:本文和幻灯片中讨论的是“时钟事件源(clock event devices)”,而我们在此期间改用了“时钟事件设备(clock event devices)”这个名称。
该设计包含以下基本构建块:
- hrtimer 基础架构
- timeofday 和时钟源管理
- 时钟事件管理
- 高精度计时器功能
- 动态 ticks
hrtimer 基础架构
-----------------------
hrtimer 基础架构已合并到 2.6.16 内核中。基础实现的详细信息请参阅 Documentation/timers/hrtimers.rst。另请参阅图 2(OLS 幻灯片第 15 页)。
与定时器轮盘(用于存放已启用的 timer_list 类型定时器)的主要区别在于:
- 按时间顺序入队到 rb-tree
- 独立于 ticks(处理基于纳秒)
timeofday和时钟源管理
-------------------------------------
John Stultz 的通用时钟 (GTOD) 框架将大量代码从架构特定区域移至通用管理框架,如图 3 所示(OLS 幻灯片第 18 页)。架构特定部分被精简为时钟源的底层硬件细节,这些时钟源在框架中注册,并根据质量决策进行选择。底层代码提供硬件设置和读取例程,并初始化数据结构,通用计时代码将使用这些数据结构将时钟滴答转换为基于纳秒的时间值。所有其他与计时相关的功能都已移至通用代码中。GTOD 基础补丁已合并到 2.6.18 内核中。
有关通用时间框架的更多信息,请参阅 OLS 2005 会议论文集第 1 卷:
http://www.linuxsymposium.org/2005/linuxsymposium_procv1.pdf //已打不开
论文“我们不再年轻:时间和计时器的新方法”由 J. Stultz、D.V. Hart 和 N. Aravamudan 撰写。
图 3(OLS 幻灯片第 18 页)展示了这种转换。
时钟事件管理
----------------------
时钟源提供对单调递增时间值的读取访问,而时钟事件设备则用于调度下一个事件中断。下一个事件目前定义为周期性事件,其周期在编译时定义。各种事件驱动功能的事件设备的设置和选择都硬编码到与架构相关的代码中。这会导致所有架构的代码重复,并且很难更改系统配置以使用架构中已内置的事件中断设备以外的其他设备。当前设计的另一个含义是,为了提供新功能(例如高分辨率计时器或动态时钟节拍),必须触及所有特定于架构的实现。
时钟事件子系统试图通过提供通用解决方案来管理时钟事件设备及其在各种时钟事件驱动内核功能中的使用,从而解决这个问题。时钟事件子系统的目标是将与时钟事件相关的、依赖于架构的代码最小化,只保留纯硬件相关的处理,并允许轻松添加和使用新的时钟事件设备。它还最大限度地减少了跨架构的重复代码,因为它提供了通用功能,直至几乎本质上依赖于硬件的中断服务处理程序。
时钟事件设备(clock event device)由与架构相关的启动代码注册,或在模块插入时注册。每个时钟事件设备都会使用特定于时钟的属性参数和回调函数填充一个数据结构。时钟事件管理通过使用指定的属性参数来决定时钟事件设备将用于支持的系统函数集。这包括区分每个 CPU 和每个系统的全局事件设备。
系统级全局事件设备用于 Linux 周期性 tick。每个 CPU 的事件设备用于提供本地 CPU 功能,例如进程记账、性能分析和高精度计时器。
管理层将以下一个或多个功能分配给时钟事件设备:
- 系统全局周期性 tick(jiffies 更新)
- CPU 本地 update_process_times
- CPU 本地性能分析
- CPU 本地下一个事件中断(非周期模式)
时钟事件设备将定时器中断相关函数的选择完全委托给管理层。时钟管理层在设备描述结构中存储一个函数指针,该指针必须由硬件级处理程序调用。这从特定于架构的定时器中断处理程序中移除了大量重复代码,并将时钟事件设备的控制权以及定时器中断相关功能的分配交给了核心代码。
时钟事件层的 API 相当精简。除了时钟事件设备注册接口外,它还提供了用于调度下一个事件中断、时钟事件设备通知服务以及支持挂起和恢复的函数。
该框架增加了约 700 行代码,导致内核二进制文件大小增加 2KB。i386 的转换减少了约 100 行代码。二进制文件大小减少了 400 字节左右。我们认为,灵活性的提升以及避免跨架构代码重复,使得二进制文件大小略有增加是合理的。
架构转换不会对功能产生影响,但允许使用高分辨率定时器和动态滴答功能,而无需更改时钟事件设备和定时器中断代码。转换后,只需将 kernel/time/Kconfig 文件添加到特定于架构的 Kconfig 文件,并将特定于动态滴答的调用添加到空闲例程中(在空闲函数和 Kconfig 文件中共添加了 3 行代码),即可启用高分辨率定时器和动态滴答。
图 4(OLS 幻灯片第 20 页)演示了转换过程。
高分辨率定时器功能
----------------------------------
系统启动期间无法使用高分辨率定时器功能,而实现此功能会非常困难,并且没有任何实际用途。必须先初始化时钟事件设备框架、时钟源框架 (GTOD) 和 hrtimers 本身,并注册相应的时钟源和时钟事件设备,然后高分辨率定时器功能才能正常工作。在 hrtimers 初始化之前,系统工作在通常的低分辨率周期模式下。时钟源和时钟事件设备层提供通知功能,用于通知 hrtimers 新硬件的可用性。hrtimers 会在切换到高分辨率模式之前验证已注册的时钟源和时钟事件设备的可用性。这也能确保配置了高分辨率定时器的内核能够在缺乏必要硬件支持的系统上运行。高分辨率定时器功能.
高分辨率计时器代码不支持仅具有全局时钟事件设备的 SMP 计算机。此类硬件的支持会在发生中断时涉及 IPI 调用。其开销将远大于其收益。因此,我们目前在 i386 SMP 系统上禁用高分辨率和动态时钟滴答,因为这些系统会在 C3 电源状态下停止本地 APIC。目前有一个可行的解决方法,但该问题尚未得到解决。
按时间顺序插入定时器提供了所有基础架构,用于决定在添加定时器时是否需要对事件设备进行重新编程。该决定是针对每个定时器基址做出的,并通过支持函数在每个 CPU 定时器基址之间进行同步。该设计允许系统为每个 CPU 定时器基址使用单独的每个 CPU 时钟事件设备,但目前每个 CPU 仅使用一个可重新编程的时钟事件设备。
当定时器中断发生时,时钟事件分配代码会调用下一个事件中断处理程序,将过期的定时器从红黑树移至单独的双向链表,并调用软中断处理程序。hrtimer 结构体中新增了一个模式字段,允许系统直接从下一个事件中断处理程序执行回调函数。这仅限于能够在硬中断上下文中安全执行的代码。例如,这适用于 nanosleep 使用的唤醒函数的常见情况。在中断上下文中执行处理程序的优势在于可以避免最多两次上下文切换——从被中断的上下文切换到软中断,再切换到被过期定时器唤醒的任务。
一旦系统切换到高分辨率模式,周期性时钟事件就会关闭。这将禁用每个系统的全局周期性时钟事件设备,例如 i386 SMP 系统上的 PIT。
周期性时钟事件功能由每个 CPU 的 hrtimer 提供。####回调函数在下一个事件中断上下文中执行,更新 jiffies 并调用 update_process_times 和性能分析。基于 hrtimer 的周期性时钟事件实现旨在扩展动态时钟事件功能。这允许在 UP 系统上使用单个时钟事件设备来调度高分辨率计时器和周期性事件(jiffies 时钟事件、性能分析、进程记账)。这已被证明可以与 i386 上的 PIT 和 PPC 上的 Incrementer 配合使用。
用于运行 hrtimer 队列和执行回调的软中断已与 tick bound timer 软中断分离,#### 以便能够准确传递 itimer 和 POSIX 间隔定时器使用的高分辨率定时器信号。此软中断的执行仍然可能被其他软中断延迟,但这种分离显著改善了整体延迟。
图 5(OLS 幻灯片第 22 页)展示了这种转变。
动态tick
-------------
动态tick是基于 hrtimer 的周期性tick替换 (sched_tick) 的逻辑结果。sched_tick hrtimer 的功能通过三个函数进行了扩展:
- hrtimer_stop_sched_tick
- hrtimer_restart_sched_tick
- hrtimer_update_jiffies
当 CPU 进入空闲状态时,会调用 hrtimer_stop_sched_tick() 函数。该函数会评估下一个已调度的定时器事件(来自 hrtimer 和定时器轮盘),如果下一个事件比下一个 tick 更远,则会将 sched_tick 重新编程为该未来事件,从而允许更长的空闲休眠时间,避免周期性 tick 带来的无意义中断。如果在空闲期间发生中断,但不会导致重新调度,也会调用该函数。调用该函数是必要的,因为中断处理程序可能已经启动了一个新的定时器,其到期时间早于上次调用 hrtimer_stop_sched_tick 时确定的最近事件时间。
当 CPU 在调用 schedule() 之前离开空闲状态时,会调用 hrtimer_restart_sched_tick()。hrtimer_restart_sched_tick() 会恢复周期性 tick,并保持活动状态直到下次调用 hrtimer_stop_sched_tick()。
当空闲期间发生中断时,会从 irq_enter() 调用 hrtimer_update_jiffies(),以确保 jiffies 保持最新状态,并且中断处理程序无需处理最终过时的 jiffy 值。
动态 tick 功能提供统计值,这些统计值通过 /proc/stat 导出到用户空间,#### 可用于增强电源管理控制。
该实现为进一步开发预留了空间,例如完全无 tick 系统(其中时间片由调度程序控制)、可变频率分析以及未来彻底移除 jiffies 等。
除了目前首次提交的 i386 支持外,该补丁集已扩展到 x86_64 和 ARM。MIPS 和 PowerPC 也已提供初步支持(正在进行中)。
Thomas, Ingo
三、ols2006-hrtimers-slides.pdf
注: 翻译自 http://www.cs.columbia.edu/~nahum/w6998/papers/ols2006-hrtimers-slides.pdf
Pg1:
hrtimers 及其他
Linux time(r) 系统的转型
Thomas Gleixner
Douglas Niehaus
OLS 2006
Pg2:
原始时间/定时器系统
Pg3:
历史
● 按到期时间排序的双向链表
● UTIME (1996)
● 定时器轮盘 (1997)
● HRT (2001)
● hrtimers (2006)
Pg4:
定时器轮
● 需要周期性tick
● O(1) 插入/删除
● 突发性重新级联(可能导致高延迟)
● 由于定时器回调持续时间长且重新级联次数增加,更高的滴答频率无法扩展
Pg5:
级联
Pg6:
级联
100 250 1000 HZ
[1] 256 10 4 1 ms
[2] 64 2560 1024 256 ms
[3] 64 164 66 16 s
[4] 64 175 70 17 m
[5] 64 186 75 19 h
Pg7:
级联
CONFIG_BASE_SMALL=y
100 250 1000 HZ
[1] 64 10 4 1 ms
[2] 16 640 256 64 ms
[3] 16 10240 4096 1024 ms
[4] 16 164 66 16 s
[5] 16 44 17 4 m
Pg8:
级联
必须谨慎选择数组大小,并考虑时钟频率。
● 罕见(多次)级联会增加延迟。
– 必须分析用例以避免出现问题级联。
● 将高精度计时器与粗粒度超时分开,可以缓解这种情况。
Pg9:
计时器 vs. 超时
计时器:
● 精确的事件调度
● 准确
● 可能过期
超时:
● 报告错误条件
● 粒度更粗
● 可能在过期前被移除
Pg10:
高精度计时器的历史
● UTIME – KURTLinux
– 堪萨斯大学
● HRT – UTIME 的分支
– Monta Vista
● Hrtimers
– Linutronix
Pg11:
为什么要使用 hrtimers?
● UTIME 和 HRT 添加了 jiffy 子字段
– 设计上保留了 jiffy ticks,以避免更广泛的内核变更影响。
– 模式:位于定时器轮盘顶部或单独的高分辨率事件列表。
● HRT 将高分辨率定时器移至到期前一个tick的单独列表中
– 定时器轮盘延迟问题
Pg12:
● 定时器插入红黑树,按到期时间排序
● 每个基准时钟单独设置队列,简化 POSIX 定时器
● 基础代码仍由 tick 驱动(软中断在定时器软中断上下文中调用)
● 时间值保存在新的数据类型 ktime_t 中(使用纳秒基数)
Pg13:
ktime_t
● 适用于 32 位和 64 位计算机的可优化数据类型
● 在 64 位 CPU 上显示纯纳秒值
● 在 32 位 CPU 上以字段顺序表示的 (秒, 纳秒) 对,允许(取决于字节序)进行 64 位加、减、比较运算。
Pg14:
hrtimer 用户
● nanosleep
● itimer
● POSIX 定时器
● 定时 futex 操作
Pg15:
hrtimers
Pg16:
如何获取高精度定时器?
● 解决滴答 (jiffy) 对timekeeping的依赖。
● 为下一个事件中断编程创建一个通用框架。
● 用 hrtimers 下的定时器替换周期性滴答中断。
Pg17:
Timekeeping
● 利用 John Stultz 的通用时间框架
– 独立于架构
– 通用框架取代了重复的架构代码
– 更好地与 tick 解耦
Pg18:
Pg19:
clockevents
● 用于分发定时器相关事件的通用基础架构
– 独立于架构
– 通用框架取代重复的架构代码
– 允许基于质量选择时钟事件硬件
Pg20:
Pg21:
滴答模拟
● 使用 perCPU hrtimer 模拟 tick
– 更新 jiffies 和 NTP 调整
– perCPU 调用
● 进程记账和分析
● 允许高分辨率计时器和/或动态滴答
Pg22:
hrtimers + GTOD + clockevents + tick emulation
Pg23:
高分辨率性能
clock_nanosleep(ABS_TIME)
间隔: 10ms
10000 次循环
无负载
Kernel min max avg
2.6.16 24 4042 1989 µs
2.6.16hrt 12 94 20 µs
2.6.16rt 6 40 10 µs
Pg24:
高分辨率性能
clock_nanosleep(ABS_TIME)
间隔: 10ms
10000 次循环
100%负载
Kernel min max avg
2.6.16 55 4280 2198 µs
2.6.16hrt 11 458 55 µs
2.6.16rt 16 55 20 µs
Pg25:
动态tick空闲行为
● 定时器中断减少至每秒约 1 次。
– 使用仪器识别定时器(滥用)用户,以改善空闲睡眠时长.
Pg26:
定时器轮批处理
● 通过跳过定时器轮处理用户空间可配置的刻数,以低于调度程序刻度的频率运行定时器轮。
● 提升交互性
Pg27:
待办事项
● 合并(目标版本 2.6.19)
● 支持更多架构
(ARM 和 PPC 的原型可用)
● 更紧密地集成到电源管理中
Pg28:
结论
● 重大改变固然必要,但其益处在于:
– 独立于架构的代码
– 易于使用各种计时和定时器事件硬件
– 按需提高预定事件的分辨率
posted on 2025-04-18 20:59 Hello-World3 阅读(20) 评论(0) 收藏 举报