实时内核补丁集
本文介绍了 Linux 内核 实时补丁集,以及一些用于排查调度延迟的实用程序。
什么是实时?
实时 应用程序在某些触发事件和应用程序对该事件的响应之间具有操作截止时间。 为了满足这些操作截止时间,程序员使用实时操作系统 (RTOS),在该操作系统上,对于给定的应用程序和环境,最大响应时间可以可靠地计算或测量。 典型的 RTOS 使用优先级。 想要 CPU 的最高优先级任务总是在唤醒任务的事件发生后的固定时间内获得 CPU。 在这样的 RTOS 上,任务的延迟仅取决于以相等或更高优先级运行的任务; 可以忽略以较低优先级运行的任务。 在非实时操作系统(大多数运行默认内核的 GNU/Linux 发行版)上,由于延迟取决于系统上运行的每个进程,因此显然更难确保每次都能满足截止时间,并且这种难度随着系统复杂性呈非线性增长。 调度中的确定性变得更加难以实现,因为可以关闭抢占任意时间。 因此,想要运行的高优先级任务可能会因禁用抢占的低优先级任务而无限期地延迟。
实时补丁如何工作
RT-Preempt 补丁将 Linux 转换为完全可抢占内核。 这是通过以下方式完成的
- 通过使用 rtmutexes 重新实现,使内核内锁定原语(使用自旋锁)可抢占。
- 受 spinlock_t 和 rwlock_t 等保护的临界区现在是可抢占的。 使用 raw_spinlock_t(与 spinlock_t 相同的 API)仍然可以创建不可抢占的区段(在内核中)。
- 为内核内自旋锁和信号量实现优先级继承。
- 将中断处理程序转换为可抢占内核线程:RT-Preempt 补丁在内核线程上下文中处理软中断处理程序,内核线程上下文由 task_struct 表示,类似于常见的用户空间进程。 但是,也可以在内核上下文中注册 IRQ。
- 将旧的 Linux 定时器 API 转换为单独的基础架构,用于高分辨率内核定时器和一个用于超时,从而产生具有高分辨率的用户空间 POSIX 定时器。
安装
有两个可用的实时补丁内核:linux-rt 和 linux-rt-lts,它们都具有基于主 linux 内核软件包的配置。 linux-rt 遵循 -rt 补丁的开发分支,而 linux-rt-lts 跟踪 rt 补丁集的稳定分支。
调度延迟
在调度器的上下文中,延迟是从事件发生到处理该事件所经过的时间。 通常是从中断触发到中断处理程序开始运行的延迟,但也可能是从定时器到期等。
高调度延迟可能有多种不同的原因。 一些值得提及的原因(没有特定的顺序)是:配置错误的系统、不良硬件、错误编程的内核模块、CPU 电源管理、有故障的硬件定时器、SMI 和 SMT。
当尝试确定系统的最大调度延迟时,需要使系统处于负载之下。 繁忙的系统往往比空闲系统经历更大的延迟。 为了充分表征感兴趣的延迟,谨慎的做法是在各种标称和最坏情况负载条件下长时间运行测试。 此外,由于磁盘、网络设备、USB 和图形等许多子系统在系统上线后可能会零星使用,因此应注意在这些子系统处于活动状态时表征延迟。
延迟测试工具
理解延迟是非直观的。 在测量和解释延迟时,即使是经验丰富的计算机科学家也常常犯错并且很可能出错。 流行的工具通常是不正确的。 这个演讲解释了一些常见的陷阱。 有几个工具可用于检查内核调度延迟,并追踪延迟峰值的原因。 一组工具包含在一个名为 rt-tests 的软件包中。
cyclictest
rt-tests 中的程序之一称为 cyclictest,可用于验证最大调度延迟,并用于追踪延迟峰值的原因。 cyclictest 的工作原理是测量线程设置的定时器到期与线程再次开始运行之间的时间。
这是典型测试运行的结果
# cyclictest --smp -p98 -m
# /dev/cpu_dma_latency set to 0us policy: fifo: loadavg: 239.09 220.49 134.53 142/1304 23799 T: 0 (23124) P:98 I:1000 C: 645663 Min: 2 Act: 4 Avg: 4 Max: 23 T: 1 (23125) P:98 I:1500 C: 430429 Min: 2 Act: 5 Avg: 3 Max: 23 T: 2 (23126) P:98 I:2000 C: 322819 Min: 2 Act: 4 Avg: 3 Max: 15 T: 3 (23127) P:98 I:2500 C: 258247 Min: 2 Act: 5 Avg: 4 Max: 32 ^C
它显示了一个四核 CPU 系统,每个内核运行一个线程 (SCHED_FIFO),优先级为 98,内存已锁定,由于在单独的终端中运行 hackbench,系统也处于高负载状态。 最有趣的是检测到的最大调度延迟,在本例中为核心 3 上的 32 微秒。
请参阅 cyclictest(8) 手册页。
hackbench
空闲内核往往会显示低得多的调度延迟,必须对其施加一些负载才能获得真实的结果。 这可以使用 rt-tests 软件包中的另一个实用程序 hackbench 来完成。 它的工作原理是创建多对线程或进程,这些线程或进程通过套接字或管道在彼此之间传递数据。 要使其运行更长时间,请添加 -l 参数:hackbench -l 1000000
。
请参阅 hackbench(8) 手册页。
hwlatdetect
hwlatdetect 可用于检测 SMI 占用过长时间,从而通过阻止正常的内核执行来引入延迟。 它由一个内核模块(linux-rt 和 linux-rt-lts 中都存在)和一个 python 脚本组成,用于启动该进程并将结果报告给用户。 要检查系统是否使用 NMI,请运行以下命令
$ grep NMI /proc/interrupts
NMI: 3335 3336 3335 3335 Non-maskable interrupts
hwlatdetect 内核模块的工作原理是通过 stop_machine() 调用关闭 CPU 上运行的所有内容。 然后,它轮询 TSC(时间戳计数器)以查找生成的数据流中的间隙。 任何间隙都表明它被 NMI 中断,因为它们是唯一可能的机制(除了损坏的 TSC 实现)。 要以 15 微秒的检测阈值运行该程序 120 秒,请执行以下操作
# hwlatdetect --duration=120 --threshold=15
hwlatdetect: test duration 120 seconds parameters: Latency threshold: 15us Sample window: 1000000us Sample width: 500000us Non-sampling period: 500000us Output File: None Starting test test finished Max Latency: 21us Samples recorded: 16 Samples exceeding threshold: 16 1408928107.0286324723 18 17 . . 1408928180.0296881126 15 21 . . 1408928212.0300332889 18 18
结果显示检测到 16 个 NMI 超过了指定的 15 微秒阈值,检测到的最大延迟为 21 微秒。
请参阅 hwlatdetect(8) 手册页。