目录
可以对 CPU 进行分区,以分离具有不同重点的任务和中断的资源。在实时系统中,可以使用 CPU 分区来分离专用于实时任务的 CPU 及其相应的中断。
CPU 分区的基础技术是 CPU 亲和性。在此机制的基础上,还实现了用于 CPU 分区的更多 Linux 内核功能。用户空间工具也可用。
本文简要概述了这些设施和工具。请点击链接查看详细信息。
亲和力
通过设置亲和性,可以将任务或中断的处理限制在指定的一组 CPU 上。任务 CPU 亲和性会影响调度程序,并确保特定任务仅在任务亲和性集中的 CPU 上执行。IRQ 亲和性指定允许将中断路由到哪个 CPU。
任务的CPU亲和性
在 SMP 系统中,操作系统调度 程序将进程或任务绑定到一个或多个处理器的属性称为 CPU 亲和性,可以覆盖调度程序将进程或任务分配给特定处理器集的方式,这是多个操作系统中提供的功能。其理念是“始终在处理器 1 上运行此进程/任务”或“在除处理器 0 之外的所有处理器上运行这些进程/任务”。调度程序将进程/任务放在亲和性集中的 CPU 上。
可以通过以下机制利用任务亲和性管理:
- cgroups: cpusets
- CPU isolation: 参考 CONFIG_CPU_ISOLATION 和 isolcpus
- System calls: sched_[get/set]affinity
- Tools: taskset
每个 CPU 线程(例如ksoftirqd/n_和_kworker/n_,其中_n_是核心数) 的 CPU 亲和性不可设置。其他线程(例如_kswapd/n)是每个 NUMA 节点的,只能固定在其节点的核心内。
CPU 亲和性和 kworkers
Kworker 线程及其执行的工作队列任务是一种特殊情况。虽然可以依赖taskset和sched\_setaffinity()来管理 kworker,但这样做没有什么用处,因为线程通常寿命很短,而且无论如何,通常会执行各种各样的工作。工作队列的范例是将亲和性设置与任务本身关联起来。“未绑定”是非per-CPU 的工作队列的名称。这些工作队列在许多系统上消耗大量 CPU 时间,并且往往会对延迟控制造成最大的管理挑战。出现在/sys/devices/virtual/workqueue中的那些未绑定工作队列可以从用户空间进行配置。参数affinity_scope、affinity_strict和_cpu_mask_共同决定执行工作功能的 kworker 将在哪些核心上运行。许多未绑定工作队列无法通过 sysfs 配置。要使它们的属性在那里可见,需要在内核源代码中增加一个WQ_SYSFS标志。
自内核 6.5 以来,tools/workqueue/wq_monitor.py Python 脚本已内置,自 6.6 以来, wq_dump.py也已加入其中。这些 Python 脚本需要drgn调试器,该调试器由主要的 Linux 发行版打包。另一个可能对实时项目特别感兴趣的最近添加的脚本是[wqlat.py ],它是bcc/tools套件的一部分(请参阅)。这两组工具可能都需要特殊的内核配置设置。
IRQ 亲和性
硬件中断可以随时中断内核和用户空间计算,除非内核禁用中断处理以保护资源。处理硬件中断时,CPU 会切换到单独的上下文并执行处理程序代码,然后切换回中断的上下文并恢复执行。
根据中断硬件,中断可以路由到任何 CPU,也可以在 CPU 之间轮流传递。大多数中断控制器允许通过设置 IRQ 亲和性来限制特定中断可以传递到的 CPU 集。
当 CPU 收到中断时,将执行上下文切换到中断上下文,当前任务必须等待,直到 IRQ 得到处理。只允许一组 CPU 处理专用 IRQ 的可能性称为 IRQ 亲和性。因此,中断到 CPU 的硬件路由会受到影响。
IRQ 亲和性管理可通过以下机制来利用:
- procfs: procfs
- Kernel command line parameter: Default IRQ affinity
- Tools: irqbalanced
- Tools: taskset
Housekeeping cores
实时系统的常见范例是将延迟不敏感的内核和用户空间任务固定在指定的“内务”核心上。例如,taskset可以固定内核线程,如kswapd和kauditd。网络流量延迟不重要的应用程序可能也希望将网络 IRQ 固定在那里。有时占用大量 CPU 的用户空间线程(如 systemd 和 rsyslog)也可以固定在内务核心上。如果用户空间线程的大部分工作是由未绑定的工作队列执行的,则固定用户空间线程不会产生预期的效果,这些工作队列可能会迁移到任何核心。
软中断和 kthreads
软中断是内核线程,在实时系统中管理起来通常比较困难。软中断可能在“引发”它们的硬中断之后立即在原子上下文中运行,也可能由每个 CPU 的内核线程(称为ksoftirqd/n,其中n是内核编号)在进程上下文中执行。软中断有 10 种,它们为网络、块、调度、计时器和RCU子系统执行各种任务,并通过 tasklet 机制为大量设备驱动程序执行回调。在内核上,任何给定时间只能有一个任何类型的软中断处于活动状态。因此,如果 ksoftirqd 被硬中断抢占,则关联的软中断将无法立即跟进,并且必须等待 ksoftirqd。实时 Linux 维护人员将这种不幸的情况称为“新的大内核锁”。
内核配置允许系统管理员将 NET_RX 和 RCU 回调从软中断移到它们自己的 kthreads 中。从内核 5.12 开始,可以通过在与网络设备关联的线程sysfs 属性中echo -ing '1' 来将 NET_RX 移到它自己的 kthread 中。之后,进程表将包含一个名为napi/xxx的新 kthread ,其中 xxx 是接口名称。[在网络 wiki 中阅读有关NAPI机制的更多信息。]用户空间可以使用_taskset_将此 kthread 固定在任何核心上。将软中断移到它自己的 kthread 中会导致上下文切换损失,但即便如此,在网络流量突发不可接受地延迟应用程序的系统上也是值得的。RCU 回调卸载会生成一组新的 kthreads,可以通过编译时配置与启动时命令行参数的组合来实现。