减少每个 CPU 的 kthread 引起的操作系统抖动

本文列出了 Linux 内核中每个 CPU 的 kthread,并提出了控制其操作系统抖动的方法。请注意,非每个 CPU 的 kthread 不在此列。为了减少非每个 CPU 的 kthread 产生的操作系统抖动,请将它们绑定到专门用于此类工作的“日常维护 (housekeeping)”CPU 上。

参考资料

  • SMP IRQ 亲和性:将中断绑定到一组 CPU。

  • Documentation/admin-guide/cgroup-v1:使用 cgroups 将任务绑定到一组 CPU。

  • man taskset:使用 taskset 命令将任务绑定到一组 CPU。

  • man sched_setaffinity:使用 sched_setaffinity() 系统调用将任务绑定到一组 CPU。

  • /sys/devices/system/cpu/cpuN/online:控制 CPU N 的热插拔状态,写入“0”表示下线,写入“1”表示上线。

  • 为了定位 CPU N 上由内核产生的操作系统抖动

    cd /sys/kernel/tracing echo 1 > max_graph_depth # 增加“1”的值以获取更多详细信息 echo function_graph > current_tracer # 运行工作负载 cat per_cpu/cpuN/trace

kthreads

名称

ehca_comp/%u

用途

定期处理 InfiniBand 相关的工作。

为了减少其操作系统抖动,请执行以下任意操作:

  1. 不要使用 eHCA InfiniBand 硬件,而是选择不需要每个 CPU 的 kthread 的硬件。这可以从一开始就防止创建这些 kthread。(这对大多数人有效,因为这种硬件虽然重要,但相对较旧且产量相对较低。)

  2. 在其他 CPU 上完成所有与 eHCA InfiniBand 相关的工作,包括中断。

  3. 改造 eHCA 驱动程序,使其每个 CPU 的 kthread 仅在选定的 CPU 上配置。

名称

irq/%d-%s

用途

处理线程化中断。

为了减少其操作系统抖动,请执行以下操作:

  1. 使用中断亲和性(irq affinity)强制 irq 线程在其他 CPU 上执行。

名称

kcmtpd_ctr_%d

用途

处理蓝牙工作。

为了减少其操作系统抖动,请执行以下操作之一:

  1. 不要使用蓝牙,在这种情况下,这些 kthread 从一开始就不会被创建。

  2. 使用中断亲和性强制蓝牙相关的中断在其他 CPU 上发生,并且在其他 CPU 上发起所有蓝牙活动。

名称

ksoftirqd/%u

用途

在线程化或高负载情况下执行软中断(softirq)处理程序。

为了减少其操作系统抖动,必须分别对每个软中断向量进行如下处理:

TIMER_SOFTIRQ

执行以下所有操作:

  1. 尽最大可能在 CPU 非空闲时使其远离内核,例如,通过避免系统调用,并强制内核线程和中断在别处执行。

  2. 使用 CONFIG_HOTPLUG_CPU=y 构建内核。引导完成后,强制 CPU 下线,然后再将其上线。这会迫使周期性定时器迁移到别处。如果您关心多个 CPU,请在将第一个 CPU 重新上线之前,将它们全部强制下线。一旦您将相关 CPU 上线,切勿将任何其他 CPU 下线,因为这样做可能会迫使定时器重新回到相关 CPU 上。

NET_TX_SOFTIRQ 和 NET_RX_SOFTIRQ

执行以下所有操作:

  1. 强制网络中断到其他 CPU 上。

  2. 在其他 CPU 上发起任何网络 I/O。

  3. 一旦您的应用程序启动,请防止从可能在要消除抖动的 CPU 上运行的任务中发起 CPU 热插拔操作。(在启动应用程序之前,强制此 CPU 下线然后再将其上线是可以的。)

BLOCK_SOFTIRQ

执行以下所有操作:

  1. 强制块设备中断到其他 CPU 上。

  2. 在其他 CPU 上发起任何块 I/O。

  3. 一旦您的应用程序启动,请防止从可能在要消除抖动的 CPU 上运行的任务中发起 CPU 热插拔操作。(在启动应用程序之前,强制此 CPU 下线然后再将其上线是可以的。)

IRQ_POLL_SOFTIRQ

执行以下所有操作:

  1. 强制块设备中断到其他 CPU 上。

  2. 在其他 CPU 上发起任何块 I/O 和块 I/O 轮询。

  3. 一旦您的应用程序启动,请防止从可能在要消除抖动的 CPU 上运行的任务中发起 CPU 热插拔操作。(在启动应用程序之前,强制此 CPU 下线然后再将其上线是可以的。)

TASKLET_SOFTIRQ

执行以下一项或多项操作:

  1. 避免使用使用 tasklet 的驱动程序。(此类驱动程序将包含对诸如 tasklet_schedule() 之类函数的调用。)

  2. 将必须使用的所有驱动程序从 tasklet 转换为工作队列 (workqueues)。

  3. 强制使用 tasklet 的驱动程序的中断到其他 CPU 上,并且在其他 CPU 上执行涉及这些驱动程序的 I/O。

SCHED_SOFTIRQ

执行以下所有操作:

  1. 避免向要消除抖动的 CPU 发送调度程序 IPI,例如,确保该 CPU 上最多只有一个可运行的 kthread。如果期望在消除抖动的 CPU 上运行的线程被唤醒,调度程序将发送一个 IPI,这可能导致随后的 SCHED_SOFTIRQ。

  2. CONFIG_NO_HZ_FULL=y,并使用“nohz_full=”引导参数确保要消除抖动的 CPU 被标记为自适应时钟滴答 (adaptive-ticks) CPU。这减少了消除抖动的 CPU 接收到的调度程序时钟中断次数,从而最大限度地降低其被选中去执行在 SCHED_SOFTIRQ 上下文中运行的负载均衡工作的几率。

  3. 尽最大可能在 CPU 非空闲时使其远离内核,例如,通过避免系统调用,并强制内核线程和中断在别处执行。这进一步减少了消除抖动的 CPU 接收到的调度程序时钟中断次数。

HRTIMER_SOFTIRQ

执行以下所有操作:

  1. 尽最大可能在 CPU 非空闲时使其远离内核。例如,避免系统调用,并强制内核线程和中断在别处执行。

  2. 使用 CONFIG_HOTPLUG_CPU=y 构建内核。引导完成后,强制 CPU 下线,然后再将其上线。这会迫使周期性定时器迁移到别处。如果您关心多个 CPU,请在将第一个 CPU 重新上线之前,将它们全部强制下线。一旦您将相关 CPU 上线,切勿将任何其他 CPU 下线,因为这样做可能会迫使定时器重新回到相关 CPU 上。

RCU_SOFTIRQ

执行以下至少一项操作:

  1. 通过执行以下所有操作,卸载回调 (callbacks) 并使 CPU 保持在动态时钟滴答空闲 (dyntick-idle) 或自适应时钟滴答 (adaptive-ticks) 状态:

    1. CONFIG_NO_HZ_FULL=y,并使用“nohz_full=”引导参数确保要消除抖动的 CPU 被标记为自适应时钟滴答 CPU。将 rcuo kthreads 绑定到能够容忍操作系统抖动的日常维护 (housekeeping) CPU 上。

    2. 尽最大可能在 CPU 非空闲时使其远离内核,例如,通过避免系统调用,并强制内核线程和中断在别处执行。

  2. 通过执行以下所有操作,使 RCU 能够通过动态时钟滴答空闲 (dyntick-idle) 远程执行其处理:

    1. 使用 CONFIG_NO_HZ=y 构建内核。

    2. 确保 CPU 频繁进入空闲状态,从而允许其他 CPU 检测到它已经通过了 RCU 静止状态 (quiescent state)。如果内核构建时带有 CONFIG_NO_HZ_FULL=y,则用户空间执行也允许其他 CPU 检测到相关 CPU 已经通过了静止状态。

    3. 尽最大可能在 CPU 非空闲时使其远离内核,例如,通过避免系统调用,并强制内核线程和中断在别处执行。

名称

kworker/%u:%d%s (cpu, id, priority)

用途

执行工作队列请求

为了减少其操作系统抖动,请执行以下任意操作:

  1. 以实时优先级运行您的工作负载,这将允许抢占 kworker 守护进程。

  2. 通过将 WQ_SYSFS 传递给该工作队列的 alloc_workqueue(),可以使特定工作队列在 sysfs 文件系统中可见。可以使用 /sys/devices/virtual/workqueue/*/cpumask sysfs 文件将此类工作队列限制在给定的 CPU 子集中。可以使用“ls /sys/devices/virtual/workqueue”显示 WQ_SYSFS 工作队列的集合。话虽如此,工作队列维护人员希望提醒大家不要在所有工作队列中盲目滥用 WQ_SYSFS。需要谨慎的原因是,添加 WQ_SYSFS 很简单,但由于 sysfs 是正式的用户/内核 API 的一部分,因此几乎不可能将其删除,即使当初添加它是一个错误。

  3. 执行以下任何必要的操作,以避免您的应用程序无法容忍的抖动:

    1. 避免使用 oprofile,从而避免来自 wq_sync_buffer() 的操作系统抖动。

    2. 限制您的 CPU 频率,以便不需要 CPU 频率调节器 (governor),可能需要借助特殊的散热片或其他冷却技术。如果操作正确,且您的 CPU 架构允许,您应该能够通过 CONFIG_CPU_FREQ=n 构建内核,以避免 CPU 频率调节器定期在每个 CPU 上运行,包括 cs_dbs_timer()od_dbs_timer()

      警告:请检查您的 CPU 规格,以确保这在您的特定系统上是安全的。

    3. 从 v3.18 开始,Christoph Lameter 的按需 vmstat 工作线程提交防止了在 CONFIG_SMP=y 系统上由 vmstat_update() 引起的操作系统抖动。在 v3.18 之前,无法完全消除操作系统抖动,但您可以通过向 /proc/sys/vm/stat_interval 写入一个较大的值来降低其频率。默认值为 HZ,时间间隔为一秒。当然,较大的值会使您的虚拟内存统计信息更新更慢。当然,您也可以以实时优先级运行您的工作负载,从而抢占 vmstat_update(),但如果您的工作负载是 CPU 密集型的,这不是一个好主意。不过,Christoph Lameter 有一个 RFC 补丁(基于 Gilad Ben-Yossef 早期的补丁),可以减少甚至消除某些工作负载的 vmstat 开销,详见 https://lore.kernel.org/r/00000140e9dfd6bd-40db3d4f-c1be-434f-8132-7820f81bb586-000000@email.amazonses.com

    4. 如果在高端 powerpc 服务器上运行,请使用 CONFIG_PPC_RTAS_DAEMON=n 构建内核。这可以防止 RTAS 守护进程每隔一秒左右在每个 CPU 上运行。(这需要编辑 Kconfig 文件,并且会破坏该平台的 RAS 功能。)这避免了由 rtas_event_scan() 函数引起的抖动。警告:请检查您的 CPU 规格,以确保这在您的特定系统上是安全的。

    5. 如果在 PowerMAC 上运行,请使用 CONFIG_PMAC_RACKMETER=n 构建内核以禁用 CPU 仪表 (CPU-meter),从而避免来自 rackmeter_do_timer() 的操作系统抖动。

名称

rcuc/%u

用途

在 CONFIG_RCU_BOOST=y 内核中执行 RCU 回调。

为了减少其操作系统抖动,请执行以下至少一项操作:

  1. 使用 CONFIG_PREEMPT=n 构建内核。这从一开始就防止创建这些 kthread,并且还免去了对 RCU 优先级提升 (priority boosting) 的需求。这种方法对于不需要高响应度的工作负载是可行的。

  2. 使用 CONFIG_RCU_BOOST=n 构建内核。这从一开始就防止创建这些 kthread。这种方法仅在您的工作负载绝不需要 RCU 优先级提升时才可行,例如,如果您确保可能在内核中执行的所有 CPU 上都有频繁的空闲时间。

  3. 使用 CONFIG_RCU_NOCB_CPU=y 构建内核,并使用 rcu_nocbs= 引导参数启动,从所有易受操作系统抖动影响的 CPU 中卸载 RCU 回调。这种方法可防止 rcuc/%u kthreads 有任何工作要做,从而使它们永远不会被唤醒。

  4. 确保 CPU 从不进入内核,特别是避免在该 CPU 上发起任何 CPU 热插拔操作。这是防止在 CPU 上排队任何回调的另一种方法,再次防止了 rcuc/%u kthreads 有任何工作要做。

名称

rcuop/%d, rcuos/%d, 和 rcuog/%d

用途

从相应的 CPU 卸载 RCU 回调。

为了减少其操作系统抖动,请执行以下至少一项操作:

  1. 使用亲和性 (affinity)、cgroups 或其他机制强制这些 kthread 在其他 CPU 上执行。

  2. 使用 CONFIG_RCU_NOCB_CPU=n 构建内核,这将从一开始就防止创建这些 kthread。但是,请注意,这不会消除操作系统抖动,而是将其转移到 RCU_SOFTIRQ。