为低延迟操作优化 RHEL


Red Hat Enterprise Linux for Real Time 10

在 RHEL 上优化 Real Time 内核以提高性能和效率

Red Hat Customer Content Services

摘要

在 RHEL for Real Time 内核中打开工作站,以在对延迟敏感的应用程序上实现持续的低延迟和可预测的响应时间。

对红帽文档提供反馈

我们感谢您对我们文档的反馈。让我们了解如何改进它。

通过 Jira 提交反馈(需要帐户)

  1. 登录到 Jira 网站。
  2. 在顶部导航栏中点 Create
  3. Summary 字段中输入描述性标题。
  4. Description 字段中输入您对改进的建议。包括文档相关部分的链接。
  5. 点对话框底部的 Create

第 1 章 RHEL 10 中的实时内核调整

延迟或响应时间是指事件和系统响应的时间。它通常以微秒为单位(swigs)测量。

对于在 Linux 环境中运行的大多数应用程序,基本的性能调优可以满足对性能提高的要求。对于延迟低、可负责且可预测的行业,红帽有一个可调整的替代内核,以便延迟满足这些要求。RHEL for Real Time 内核提供与 RHEL 10 的无缝集成,并为客户提供在机构中测量、配置和记录延迟时间的机会。

在精心调优的系统上使用 RHEL for Real Time 内核,适用于具有非常高确定性要求的应用程序。通过内核系统调优,您可以在确定性方面获得良好改进。开始之前,对标准 RHEL 10 系统执行常规系统调优,然后部署 RHEL for Real Time 内核。

警告

如果无法执行这些任务,则可能会阻止 RHEL Real Time 部署具有一致的性能。

1.1. 调优指南

  • 实时调优是一个迭代过程;您几乎无法调整几个变量,并且知道该更改是最佳实现的。准备花费几天或周时间,缩减最适合您系统的调优配置集合。

    另外,始终使长时间测试运行。更改一些调优参数后,执行五分钟测试运行并不是一组特定的调优更改的良好验证。使测试的长度可调整运行,并在几分钟后运行它们。您可以缩小到几个不同的调优配置集,测试会运行几小时,然后一次运行这些集合,以检测最高延迟或资源耗尽的基线的情况。

  • 在应用程序中构建测量机制,以便您可以准确衡量一组特定的调优更改会影响应用程序的性能。例如,Aecdotal 证据(例如,"鼠标移动更加顺畅"通常是错误的,可能会有所不同)。进行硬测量并记录它们以便稍后进行分析。
  • 在测试运行之间对调整变量进行多次更改非常困难,但这样做意味着您没有办法缩小影响您的测试结果的调优参数。保持测试之间的调优更改尽可能小运行。
  • 在调整时,也会尝试进行大量更改,但几乎总是最好进行增量更改。您会发现,从最低到最高优先级值开始的工作将会产生长远的运行结果。
  • 使用可用的工具。tuna 调优工具可让您轻松更改线程和中断的处理器相关性、线程优先级和隔离处理器以供应用使用。tasksetchrt 命令行工具允许您执行大多数 tuna 的作用。如果您遇到性能问题,ftraceperf 工具可帮助查找延迟问题。
  • 使用外部工具更改策略、优先级和关联性,而不是硬编码的值。通过使用外部工具,您可以尝试许多不同的组合并简化您的逻辑。找到一些提供良好结果的设置后,您可以将它们添加到应用程序中,或者设置启动逻辑以在应用程序启动时实施设置。

1.2. 线程调度策略

Linux 使用三个主要线程调度策略。

  • SCHED_OTHER (有时称为 SCHED_NORMAL

    这是默认的线程策略,具有由内核控制的动态优先级。优先级会根据线程活动更改。具有此策略的线程被视为实时优先级 0 (零)。

  • SCHED_FIFO (首先为 out)

    优先级范围为 1 - 99 的实时策略,1 为最低,99 为最高。SCHED_FIFO 线程的优先级始终高于 SCHED_OTHER 线程(例如,优先级为 1SCHED_FIFO 线程将具有高于 任何 SCHED_OTHER 线程的优先级)。作为 SCHED_FIFO 线程创建的任何线程都具有固定优先级,并将运行,直到被优先级更高的线程阻止或抢占为止。

  • SCHED_RR (Round-Robin)

    SCHED_RR 是对 SCHED_FIFO 的修改。具有相同优先级的线程具有量子,并且在所有优先级 SCHED_RR 线程之间进行循环调度。此策略很少使用。

1.3. 平衡日志记录参数

syslog 服务器通过网络转发来自程序的日志消息。发生这种情况的频率较少,待处理的事务越大。如果事务非常大,可能会导致 I/O spike。要防止这种情况,请保持间隔小。

系统日志记录守护进程 syslogd 用于从不同程序收集信息。它还从内核日志记录守护进程 klogd 收集内核报告的信息。通常,syslogd 记录到本地文件,但也可以将其配置为通过网络记录到远程记录服务器。

流程

启用远程日志记录:

  1. 配置将日志发送到的计算机。如需更多信息,请参阅 Red Hat Enterprise Linux 中的 rsyslog 远程 Syslogging
  2. 配置将将日志发送到远程日志服务器的每个系统,以便其 syslog 输出写入服务器,而不是写入本地文件系统。为此,请编辑每个客户端系统上的 /etc/rsyslog.conf 文件。对于该文件中定义的每个日志记录规则,请将本地日志文件替换为远程记录服务器的地址。

    # Log all kernel messages to remote logging host.
    kern.*     @my.remote.logging.server
    Copy to Clipboard

    上面的示例将客户端系统配置为将所有内核消息记录到位于 @my.remote.logging.server 的远程机器中。

    另外,您可以通过在 /etc/rsyslog.conf 文件中添加以下行,将 syslogd 配置为记录所有本地生成的系统信息:

    # Log all messages to a remote logging server:
    .     @my.remote.logging.server
    Copy to Clipboard
重要

syslogd 守护进程不包括其生成的网络流量的内置速率限制。因此,红帽建议在使用 RHEL for Real Time 系统时,只包括您的机构远程日志记录所需的日志消息。例如:内核警告、身份验证请求等。其他消息应在本地记录。

1.4. 通过避免运行不必要的应用程序来提高性能

每个运行的应用程序都使用系统资源。确保系统上没有运行不必要的应用程序可能会显著提高性能。

先决条件

  • 您在系统上具有 root 权限。

流程

  1. 不要运行 图形界面,而这并不是绝对需要的,特别是在服务器中。

    检查系统是否默认引导进入 GUI:

    # systemctl get-default
    Copy to Clipboard
  2. 如果命令的输出是 graphical.target,请将系统配置为引导到文本模式:

    # systemctl set-default multi-user.target
    Copy to Clipboard
  3. 除非您要调整的系统上主动使用 邮件传输代理(MTA),请禁用它。如果需要 MTA,请确保其经过精心调优,或考虑将其移至专用机器。

    如需更多信息,请参阅 MTA 文档。

    重要

    MTA 用于发送由 cron 等程序执行的系统生成的消息。这包括日志函数(如 logwatch () )生成的报告。如果机器上的 MTA 已禁用,您将无法接收这些邮件。

  4. 外设设备 (如鼠标、键盘、webcams )发送可能会影响延迟的中断。如果您不使用图形界面,请删除所有未使用的外设设备并禁用它们。

    如需更多信息,请参阅设备的文档。

  5. 检查可能会影响性能的自动 cron 作业。

    # crontab -l
    Copy to Clipboard

    禁用 crond 服务或任何不需要的 cron 作业。

  6. 检查您的系统是否有第三方应用程序和外部硬件供应商添加的任何组件,并删除所有不必要的组件。

其他资源

  • 您系统上的 cron (8) 手册页

1.5. 非统一内存访问

taskset 工具仅适用于 CPU 关联性,且不知道内存节点等其他 NUMA 资源。如果要与 NUMA 结合使用进程绑定,请使用 numactl 命令而不是 taskset

有关 NUMA API 的更多信息,请参阅 Andi Kleen's 白皮书 Linux 的 NUMA API。

1.6. 确保 debugfs 已被挂载

debugfs 文件系统特别设计用于调试和为用户提供信息。它在 RHEL 8 中自动挂载到 /sys/kernel/debug/ 目录中。

注意

debugfs 文件系统使用 ftracetrace-cmd 命令挂载。

流程

验证 debugfs 是否已挂载:

  • 运行以下命令:

    # mount | grep ^debugfs
    debugfs on /sys/kernel/debug type debugfs (rw,nosuid,nodev,noexec,relatime,seclabel)
    Copy to Clipboard

    如果挂载了 debugfs,命令会显示 debugfs 的挂载点和属性。

    如果没有挂载 debugfs,命令不会返回任何内容。

1.7. RHEL for Real Time 中的 InfiniBand

InfiniBand 是一种通信架构,通常用于提高带宽、提高服务质量(QOS)并提供故障转移。它还可使用 Remote Direct Memory Access (RDMA)机制来改进延迟。

在 RHEL for Real Time 上支持 InfiniBand 与 Red Hat Enterprise Linux 10 中的支持相同。如需更多信息,请参阅配置 InfiniBand 和 RDMA 网络

1.8. 使用 RoCEE 和高性能网络

RoCEE (通过融合增强以太网的RDMA)是通过以太网网络实现远程直接内存访问(RDMA)的协议。它允许您在数据中心中维护一致、高速的环境,同时为关键事务提供确定性、低延迟数据传输。

高性能网络 (HPN)是一组共享库,可在内核中提供 RoCEE 接口。HPN 不通过独立的网络基础架构,而是使用标准以太网基础架构将数据直接放入远程系统内存中,从而减少了 CPU 开销并降低基础架构成本。

在 RHEL for Real Time 下支持 RoCEEHPN 与 RHEL 10 提供的支持不同。

1.9. 为 RHEL 实时调优容器

第 2 章 RHEL for Real Time 的调度策略

在实时中,调度程序是用来决定要运行的线程的内核组件。每个线程都有一个关联的调度策略和静态调度优先级,称为 sched_priority。调度是抢占的,因此当具有较高静态优先级的线程准备好运行时,当前运行的线程将停止。然后,运行的线程会返回到其静态优先级的 waitlist

所有 Linux 线程都有以下调度策略之一:

  • SCHED_OTHERSCHED_NORMAL : 是默认策略。
  • SCHED_BATCH: 与 SCHED_OTHER 类似,但具有增量性调整。
  • SCHED_IDLE: 是优先级低于 SCHED_OTHER 的策略。
  • SCHED_FIFO: 是第一个 in 和第一个实时策略。
  • SCHED_RR: 是循环实时策略。
  • SCHED_DEADLINE: 是根据作业截止时间排列任务优先级的调度程序策略。最早的绝对期限的作业首先运行。

2.1. 调度程序策略

实时线程的优先级高于标准线程。策略具有调度优先级值,范围从最小值为 1 到最大值 99。

以下策略对实时至关重要:

  • SCHED_OTHERSCHED_NORMAL 策略

    这是 Linux 线程的默认调度策略。它有一个动态优先级,系统会根据线程的特性更改。SCHED_OTHER 线程在 -20 之间具有 nice 值,即最高优先级和 19,这是最低优先级。SCHED_OTHER 线程的默认 nice 值是 0。

  • SCHED_FIFO 策略

    具有 SCHED_FIFO 的线程与 SCHED_OTHER 任务相比具有更高的优先级。SCHED_FIFO 不使用 nice 值,而是使用 1 之间的固定优先级,即最低优先级和 99,这是最高。优先级为 1 的 SCHED_FIFO 线程始终在 SCHED_OTHER 线程上调度。

  • SCHED_RR 策略

    SCHED_RR 策略与 SCHED_FIFO 策略类似。等于优先级的线程以轮循方式调度。SCHED_FIFOSCHED_RR 线程运行,直到发生以下事件之一:

    • 线程进入睡眠状态或等待事件。
    • 高优先级的实时线程准备好运行。

      除非发生上述事件之一,否则线程在指定的处理器中无限期运行,而较低优先级线程保留在等待运行的队列中。这可能导致系统服务线程处于驻留状态,并阻止交换出文件系统数据并导致文件系统数据清除失败。

  • SCHED_DEADLINE 策略

    SCHED_DEADLINE 策略指定时间要求。它根据任务的截止时间调度每个任务。预计截止时间最早(EDF)调度的任务首先运行。

    内核需要 runtime swigdeadlineCloneperiod 变为 true。所需选项之间的关系是 runtime swigdeadline swigperiod

2.2. SCHED_DEADLINE 策略的参数

每个 SCHED_DEADLINE 任务都是 以句点运行时和 截止时间 参数的特征。这些参数的值为纳秒的整数。

表 2.1. SCHED_DEADLINE 参数
参数描述

周期

period 是实时任务的激活模式。

例如,如果视频处理任务每秒处理 60 帧,每 16 毫秒为服务排队。因此,周期 为 16 毫秒。

runtime

runtime 是分配给任务生成输出的 CPU 执行时间量。在实时中,最大执行时间也称为"Worst Case Execution Time" (WCET)是 运行时

例如,如果视频处理工具在最糟糕的情况下,处理图像的五毫秒,则 运行时 为 5 毫秒。

deadline

deadline 是要生成输出的最长时间。

例如,如果任务需要在十毫秒内提供已处理帧,截止时间 为十毫秒。

2.3. 配置 SCHED_DEADLINE 参数

Red Hat Enterprise Linux 中的 sched_deadline_period_max_ussched_deadline_period_min_us 参数是 SCHED_DEADLINE 调度策略的内核可调参数。这些参数通过使用此实时调度类来控制任务的最大允许周期(以微秒为单位)。

sched_deadline_period_max_ussched_deadline_period_min_us 一起工作,为 SCHED_DEADLINE 任务的 period 值定义一个可接受的范围。

  • min_us 可防止可能使用过量资源的高频率任务。
  • max_us 会阻止极长的任务,它们可能会导致其他任务的性能。
注意

使用参数的默认配置。如果需要更改参数的值,您必须在实时环境中配置自定义值前测试它们。

参数的值以微秒为单位。例如,1 秒等于 100000 微秒。

先决条件

  • 您的系统必须具有 root 权限。

流程

  1. 使用其中一个 sysctl 命令临时设置所需的值。

    • 要使用 sched_deadline_period_max_us 参数,请运行以下命令:

      # sysctl -w kernel.sched_deadline_period_max_us=2000000
      Copy to Clipboard
    • 要使用 sched_deadline_period_min_us 参数,请运行以下命令:

      # sysctl -w kernel.sched_deadline_period_min_us=100
      Copy to Clipboard
  2. 永久设置值。

    • 对于 max_us,编辑 /etc/sysctl.conf 并添加以下行:

      kernel.sched_deadline_period_max_us = 2000000
      Copy to Clipboard
    • 对于 min_us,请编辑 /etc/sysctl.conf 并添加以下行:

      kernel.sched_deadline_period_min_us = 100
      Copy to Clipboard
  3. 应用更改:

    # sysctl -p
    Copy to Clipboard

验证

  • 验证 max_us 的自定义值:

    $ cat /proc/sys/kernel/sched_deadline_period_max_us
    2000000
    Copy to Clipboard
  • 验证 min_us 的自定义值:

    $ cat /proc/sys/kernel/sched_deadline_period_min_us
    100
    Copy to Clipboard

第 3 章 设置持久性内核调整参数

当您决定用于系统的调优配置时,您可以在重新启动后保留所做的更改。

默认情况下,编辑的内核调优参数仅保持有效,直到系统重启或参数被明确更改为止。这可用于建立初始调优配置。它还提供了一个安全机制。如果编辑过的参数导致计算机的行为正常,则重新启动计算机会将参数返回到以前的配置。

3.1. 进行持久性内核调整参数更改

您可以通过在 /etc/sysctl.conf 文件中添加参数来更改内核调整参数。

注意

这个过程 不会更改 当前会话中的任何内核调整参数。在 /etc/sysctl.conf 中输入的更改只会影响将来的会话。

先决条件

  • 您在系统上具有 root 权限。

流程

  1. 在文本编辑器中打开 /etc/sysctl.conf
  2. 使用参数的值将新条目插入到文件中。

    通过删除 /proc/sys/ 路径,将剩余的斜杠(/)更改为句点(.),并包含参数的值来修改参数名称。

    例如,要使命令 echo 0 > /proc/sys/kernel/hung_task_panic 持久,请输入以下内容:

    # Enable gettimeofday(2)
    kernel.hung_task_panic = 0
    Copy to Clipboard
  3. 保存并关闭该文件。
  4. 重启系统以使更改生效。

验证

  • 验证配置:

    # cat /proc/sys/kernel/hung_task_panic
    0
    Copy to Clipboard

第 4 章 应用程序调整和部署

使用最佳配置和设置组合调整实时内核可帮助增强和开发 RHEL for Real Time 应用程序。

注意

通常,尝试使用 POSIX 定义的 API (应用程序编程接口)。RHEL for Real Time 符合 POSIX 标准。RHEL for Real Time 内核减少的延迟也基于 POSIX

4.1. 实时应用中的信号处理

传统 UNIXPOSIX 信号有其用途,特别是用于错误处理,但它们不适合实时应用程序中作为事件交付机制。这是因为当前的 Linux 内核信号处理代码非常复杂,主要是因为旧行为和需要支持的许多 API。这种复杂性意味着,在提供信号时所用的代码路径并非始终最佳,应用程序可能会经历较长的延迟。

UNIX 信号后的原始动机是执行的不同"线程"之间的多路控制(进程)。信号的行为与操作系统中断类似。也就是说,当向应用发送信号时,应用的上下文会被保存,并开始执行之前注册的信号处理程序。信号处理程序完成后,应用将返回到在发送信号时执行它的位置。在实践中,这可能会变得复杂。

在实时应用程序中,信号无法信任。一个更好的选择是使用 POSIX 线程(线程)来分发工作负载,并在各种组件之间进行通信。您可以使用 mutexes、条件变量和障碍的 pthreads 机制协调线程组。这些相对新结构的代码路径比传统的处理代码更加干净。

4.2. 同步线程

sched_yield 函数是一种同步机制,允许较低优先级线程有机会运行。在较差的应用程序中发出不良的应用程序时,这种类型的请求容易出错。

较高的优先级线程可以调用 sched_yield (),以允许其他线程有机会运行。调用进程将移到该优先级中运行的进程的尾部。当发生这种情况时,如果没有其他进程以同一优先级运行,调用进程将继续运行。如果该进程的优先级很高,它可能会创建一个忙碌的循环,从而导致机器无法使用。

SCHED_DEADLINE 任务调用 sched_yield () 时,它会提供配置的 CPU,剩余的运行时会立即节流,直到下一个周期为止。sched_yield () 行为允许任务在下一个时间段内唤醒。

调度程序可以更好地决定何时以及是否实际还有其他线程等待运行。避免在任何实时任务中使用 sched_yield ()

流程

  • 要调用 sched_yield () 函数,请运行以下代码:

    for(;;) {
      do_the_computation();
      /*
       * Notify the scheduler at the end of the computation
       * This syscall will block until the next replenishment
       */
      sched_yield();
    }
    Copy to Clipboard

    SCHED_DEADLINE 任务受基于冲突的搜索(CBS)算法节流,直到下一个周期(在循环的下一次执行开始)。

4.3. 实时调度程序优先级

systemd 命令可用于在引导过程中为启动的服务设置实时优先级。一些内核线程可以被赋予一个非常高的优先级。这允许默认优先级与 Java (RTSJ)实时规范的要求良好集成。RTSJ 需要 10 到 89 范围内的优先级。

对于不使用 RTSJ 的部署,应用程序可以使用在 90 以下的调度优先级。在调度优先级 49 的任何应用程序线程时,请特别小心,因为它可以防止必要的系统服务运行,因为它可以防止重要的系统服务运行。这可能会导致无法预计的行为,包括阻止网络流量、阻止虚拟内存分页和数据崩溃,因为文件系统日志记录造成数据崩溃。

如果任何应用程序线程调度到优先级 89,请确保线程仅运行非常短的代码路径。如果不这样做,会降低 RHEL for Real Time 内核的低延迟功能。

为没有强制权限的用户设置实时优先级

默认情况下,只有具有应用 root 权限的用户才能更改优先级和调度信息。要提供 root 权限,您可以修改设置,首选方法是将用户添加到 realtime 组中。

重要

您还可以通过编辑 /etc/security/limits.conf 文件来更改用户权限。但是,这可能会导致重复,并使系统对常规用户不可用。如果您决定编辑此文件,请谨慎操作,并在进行更改之前创建副本。

4.4. 加载动态库

在开发实时应用程序时,请考虑在启动时解析符号,以避免程序执行过程中出现非确定的延迟。在启动时解析符号可能会减慢程序初始化的速度。您可以通过使用 ld.so 设置 LD_BIND_NOW 变量(动态链接器/加载器)来指示动态 Libraries 在应用程序启动时加载。

例如,以下 shell 脚本使用值 1 导出 LD_BIND_NOW 变量,然后运行具有 FIFO 的调度程序策略和优先级 1 的程序。

#!/bin/sh

LD_BIND_NOW=1
export LD_BIND_NOW

chrt --fifo 1 /opt/myapp/myapp-server &
Copy to Clipboard

第 5 章 为系统调整设置 BIOS 参数

BIOS 在系统正常工作过程中扮演了关键角色。通过正确配置 BIOS 参数,您可以显著提高系统性能。

注意

每个系统和 BIOS 供应商使用不同的术语和导航方法。有关 BIOS 设置的更多信息,请参阅 BIOS 文档或联系 BIOS 供应商。

5.1. 禁用电源管理以改进响应时间

BIOS 电源管理选项通过更改系统时钟频率或将 CPU 置于各种睡眠状态之一来节省电源。这些操作可能会影响系统响应外部事件的速度。

要改进响应时间,请在 BIOS 中禁用所有电源管理选项。

5.2. 禁用错误检测和更正单元来提高响应时间

错误检测和修正(EDAC)单元是用于检测和更正来自 Error Correcting Code (ECC)内存的错误的设备。通常 EDAC 选项的范围包括没有 ECC 检查所有内存节点的定期扫描错误。EDAC 级别越长,BIOS 使用的时间越长。这可能会导致缺少关键事件期限。

要改进响应时间,请关闭 EDAC。如果这不可能,请将 EDAC 配置为最低功能级别。

5.3. 通过配置系统管理中断来缩短响应时间

系统管理中断(SMI)是一个硬件厂商工具,可确保系统正常运行。BIOS 代码通常服务 SMI 中断。SMI 通常用于热管理、远程控制台管理(IPMI)、EDAC 检查和各种其他内务任务。

如果 BIOS 包含 SMI 选项,请检查厂商和相关文档,以确定可以安全地禁用它们的范围。

警告

虽然可以完全禁用 SMI,但红帽强烈建议您不要这样做。删除系统以生成和服务 SMI 的功能可能会导致严重硬件故障。

第 6 章 实时内核运行时验证

运行时验证是一种轻量级且严格的方法,用于检查系统事件及其正式规格之间的行为等效性。运行时验证有集成在附加到 tracepoints 的内核中的监控。如果系统状态与定义的规格不同,则运行时验证程序会激活响应器来通知或启用反应,如在日志文件或系统关闭时捕获事件以防止极端情况下的传播。

6.1. 运行时监控和响应器

运行时验证(RV)监控器封装在 RV 监控抽象中,并在定义的规格和内核追踪之间协调,以在 trace 文件中捕获运行时事件。RV 监控器包括:

  • 参考模型是系统的参考模型。
  • monitor 实例是 monitor 的一组实例,如每个 CPU 监视器或每个任务监控器。
  • 将监控器连接到系统的帮助程序功能。

除了在运行时验证和监控系统外,您还可以启用对意外系统事件的响应。反应形式的反应可能与在 trace 文件中捕获事件不同,以启动非常反应,如关闭,以避免在安全关键系统上出现系统故障。

Reactors 是 RV 监视器可用的反应方法,根据需要定义对系统事件的反应。默认情况下,监控器提供操作的追踪输出。

6.2. 在线运行时监控器

运行时验证(RV)监控器分为以下类型:

  • 在系统运行时,在线监控 trace 中的捕获事件。

    如果事件处理附加到系统执行中,则在线监视器会同步。这将在事件监控期间阻止系统。如果执行与系统分离,且在不同机器上运行,在线监视器是异步的。但是,这需要保存的执行日志文件。

  • 离线监控事件发生后生成的进程跟踪。

    通过从持久性存储读取保存的 trace 日志文件,离线运行时验证捕获信息。只有在文件中保存了事件时,离线 monitor 才能正常工作。

6.3. 用户界面

用户界面位于 /sys/kernel/tracing/rv,类似于追踪接口。用户界面包含上述文件和文件夹。

设置描述示例命令

available_monitors

显示每行一个可用的监视器。

# cat available_monitors

available_reactors

显示每行一个可用的响应器。

# cat available_reactors

enabled_monitors

显示已启用的 monitor 每行一个。您可以同时启用多个 monitor。

使用 '!' 前缀编写 monitor 名称将禁用监控并截断文件会禁用所有已启用的 monitor。

# cat enabled_monitors

# echo wip > enabled_monitors

# echo '!wip'>> enabled_monitors

monitors/

monitor/ 目录与 tracefs 文件系统上的 事件 目录类似,每个 monitor 在 monitor/ 中都有自己的目录。

# CD monitors/wip/

monitor/MONITOR/reactors

使用 "[]" 中特定 MONITOR 选择的反应者列出可用的响应器。默认为 no operation (nop) reactor。

编写响应器的名称,将它集成到特定的 MONITOR 中。

# cat monitors/wip/reactors

monitoring_on

在 trace 界面中启动 tracing_ontracing_off 开关器。

编写 0 会停止监控,1 将继续监控。switcher 不会禁用启用的监控器,而是停止每个实体监控器监控事件。

 

reacting_on

启用 reactors。编写 0 可禁用 reactions 和 1 启用 reactions。

 

monitor/MONITOR/desc

显示 Monitor 描述

 

monitor/MONITOR/enable

显示 monitor 的当前状态。编写 0 可禁用 monitor 并启用 monitor。

 

第 7 章 运行并解释硬件和固件延迟测试

使用 hwlatdetect 程序,您可以测试并验证潜在的硬件平台是否适合使用实时操作。

先决条件

  • 确保安装了 RHEL-RT (RHEL for Real Time)和 realtime-tests 软件包。
  • 有关低延迟操作所需的任何调整步骤,请查看厂商文档。

    供应商文档可以提供减少或删除任何将系统转换为系统管理模式(SMM)的系统管理中断(SMI)的说明。虽然系统处于 SMM,但它运行固件而不是操作系统代码。这意味着,在 SMM 等待期间过期的任何计时器,直到系统转换为正常操作为止。这可能导致不解释的延迟,因为 Linux 无法阻止 SMI,并且唯一表示我们实际在特定于供应商的性能计数器寄存器中可以找到 SMI。

    警告

    红帽强烈建议您不要完全禁用 SMI,因为它可能会导致出现灾难性硬件故障。

7.1. 运行硬件和固件延迟测试

在运行 hwlatdetect 程序时,不需要在系统中运行任何负载,因为测试会查找硬件架构或 BIOS 或 EFI 固件带来的延迟。hwlatdetect 的默认值是每秒轮询 0.5 秒,并报告连续调用之间超过 10 微秒的差距,以获取时间。hwlatdetect 返回 系统上可能的最大延迟。因此,如果您有一个应用程序需要最大延迟值小于 10us,hwlatdetect 会报告其中一个差距为 20us,则系统只能保证延迟 20us。

注意

如果 hwlatdetect 显示系统无法满足应用程序的延迟要求,请尝试更改 BIOS 设置或使用系统供应商获得满足应用程序延迟要求的新固件。

流程

  • 运行 hwlatdetect,以秒为单位指定测试持续时间。

    hwlatdetect 通过轮询时钟源并查找无法解释的差距来查找硬件和固件延迟。

    # hwlatdetect --duration=60s
    hwlatdetect:  test duration 60 seconds
    	detector: tracer
    	parameters:
    		Latency threshold:    10us
    		Sample window:        1000000us
    		Sample width:         500000us
    		Non-sampling period:  500000us
    		Output File:          None
    
    Starting test
    test finished
    Max Latency: Below threshold
    Samples recorded: 0
    Samples exceeding threshold: 0
    Copy to Clipboard

7.2. 解释硬件和固件延迟测试结果

硬件延迟检测器(hwlatdetect)使用 tracer 机制来检测硬件架构或 BIOS/EFI 固件带来的延迟。通过检查 hwlatdetect 测量的延迟,您可以确定潜在的硬件是否适合支持 RHEL for Real Time 内核。

例子

  • 示例结果表示系统经过调优,以最大程度降低固件系统中断。在这种情况下,hwlatdetect 的输出类似如下:

    # hwlatdetect --duration=60s
    hwlatdetect:  test duration 60 seconds
    	detector: tracer
    	parameters:
    		Latency threshold: 10us
    		Sample window:     1000000us
    		Sample width:      500000us
    		Non-sampling period:  500000us
    		Output File:       None
    
    Starting test
    test finished
    Max Latency: Below threshold
    Samples recorded: 0
    Samples exceeding threshold: 0
    Copy to Clipboard
  • 示例结果代表了一个无法调优的系统,以便最大程度降低固件的系统中断。在这种情况下,hwlatdetect 的输出类似如下:

    # hwlatdetect --duration=10s
    hwlatdetect:  test duration 10 seconds
    	detector: tracer
    	parameters:
    		Latency threshold: 10us
    		Sample window:     1000000us
    		Sample width:      500000us
    		Non-sampling period:  500000us
    		Output File:       None
    
    Starting test
    test finished
    Max Latency: 18us
    Samples recorded: 10
    Samples exceeding threshold: 10
    SMIs during run: 0
    ts: 1519674281.220664736, inner:17, outer:15
    ts: 1519674282.721666674, inner:18, outer:17
    ts: 1519674283.722667966, inner:16, outer:17
    ts: 1519674284.723669259, inner:17, outer:18
    ts: 1519674285.724670551, inner:16, outer:17
    ts: 1519674286.725671843, inner:17, outer:17
    ts: 1519674287.726673136, inner:17, outer:16
    ts: 1519674288.727674428, inner:16, outer:18
    ts: 1519674289.728675721, inner:17, outer:17
    ts: 1519674290.729677013, inner:18, outer:17----
    Copy to Clipboard

    输出显示,在连续读取 系统时钟源 的过程中有 10 个延迟,在 15-18 us 范围内显示有 10 个延迟。

    注意

    之前的版本使用内核模块而不是 ftrace tracer。

了解结果

有关测试方法、参数和结果的信息可帮助您了解延迟参数和 hwlatdetect 工具检测到的延迟值。

测试方法、参数和结果的表列出了 hwlatdetect 工具检测到的参数和延迟值。

表 7.1. 测试方法、参数和结果
参数value描述

测试持续时间

10 秒

测试的持续时间(以秒为单位)

Detector

tracer

运行 检测 器线程的实用程序

参数

  

延迟阈值

10us

允许的最大延迟

示例窗口

1000000us

1 秒

宽度示例

500000us

0.05 秒

非 Sampling 周期

500000us

0.05 秒

输出文件

None

保存输出的文件。

结果

  

最大延迟数

18us

测试期间超过 Latency 阈值的最大延迟。如果没有超过 Latency 阈值,则报告会显示 以下阈值

记录的示例

10

测试记录的样本数量。

示例超过阈值

10

由延迟超过 Latency 阈值 的测试记录的示例数量。

运行过程中的 SMI

0

测试运行期间发生的系统管理中断(SMI)的数量。

注意

hwlatdetect 工具为 inner 和 outer 输出的值是最大延迟值。它们是连续读取当前系统时钟源(通常为 TSC 或 TSC 寄存器,但可能是 HPET 或 ACPI 电源管理时钟)之间的增量,以及硬件固件组合引入连续读取之间的任何延迟。

找到合适的 hardware-firmware 组合后,下一步是在负载下测试系统的实时性能。

第 8 章 运行和解释系统延迟测试

RHEL for Real Time 提供 rteval 工具,用来在负载下测试系统实时性能。

8.1. 运行系统延迟测试

使用 rteval 工具,您可以在负载下测试系统的实时性能。

先决条件

  • 已安装 RHEL for Real Time 软件包组。
  • 您在系统上具有 root 权限。

流程

  • 运行 rteval 工具。

    # rteval
    Copy to Clipboard

    rteval 工具启动 SCHED_OTHER 任务的大量系统负载。然后,它会对每个在线 CPU 测量实时响应。负载是循环中 Linux 内核树的并行形式,而 hackbench 合成了基准。

    目标是使系统进入 状态,每个内核始终都有一个要调度的作业。作业会执行各种任务,如内存分配/自由、磁盘 I/O、计算任务、内存副本等。

    加载开始后,rt eval 会启动 cyclictest measurement 程序。该程序在每个在线内核上启动 SCHED_FIFO 实时线程。然后,它会测量实时调度响应时间。

    每个测量线程都需要一个时间戳,休眠间隔,然后在唤醒后需要另一个时间戳。测量的延迟是 t1 -(t0 + i),这是实际唤醒时间 t1 之间的差别,以及第一个时间戳 t0 的理论唤醒时间加上 sleep 间隔 i i。

    rteval 运行的详情会写入 XML 文件以及系统的引导日志。此报告显示在屏幕上,并保存到压缩文件中。

    文件名采用 rteval- <date>-N-tar.bz2 格式,其中 <date > 是生成报告的日期,N 是在 < date > 上运行 Nth 的计数器。

    以下是 rteval 报告的示例:

    System:
    Statistics:
    	Samples:           1440463955
    	Mean:              4.40624790712us
    	Median:            0.0us
    	Mode:              4us
    	Range:             54us
    	Min:               2us
    	Max:               56us
    	Mean Absolute Dev: 1.0776661507us
    	Std.dev:           1.81821060672us
    
    CPU core 0       Priority: 95
    Statistics:
    	Samples:           36011847
    	Mean:              5.46434910711us
    	Median:            4us
    	Mode:              4us
    	Range:             38us
    	Min:               2us
    	Max:               40us
    	Mean Absolute Dev: 2.13785341159us
    	Std.dev:           3.50155558554us
    Copy to Clipboard

    报告包括系统硬件、运行长度、所使用的选项以及时间结果的详细信息,包括每个cpu 和系统范围内的时间结果。

    注意

    要从其生成的文件中重新生成 rteval 报告,请运行

    # rteval --summarize rteval- &lt;date&gt;-N.tar.bz2

第 9 章 使用 rteval 容器进行实时任务执行

Red Hat Enterprise Linux (RHEL) for Real Time 中的 rteval (实时评估)容器可确保关键任务的低延迟执行。它测量各种系统负载下的计时器唤醒时间,以保持实时响应并确保及时的任务执行。

rteval 工具将测量过程(使用 cyclictestrtla)设置为高优先级任务。这种测量进程的优先级高于计算机上生成的负载。因此,rteval 容器会测量不同负载下实时任务的唤醒时间,确保系统可以有效地处理实时工作负载。

9.1. 为 rteval 容器测试主机

要在对延迟敏感的工作负载上运行 rteval 容器,您必须调整主机机器,因为容器技术不需要虚拟化堆栈中的其他内核。大多数适用于裸机的调优策略也适用于容器环境。

您必须使用 tuned-adm realtime -variables.conf 文件中定义的默认参数来应用 realtime 配置集。

realtime 配置集执行以下任务:

  • 设置各种内核命令行选项。
  • 检测非统一内存访问(NUMA)拓扑。
  • 当存在多个 NUMA 节点时,将除每个节点的第一个 CPU 之外的所有 CPU 分配给 isolcpus 设置。

rteval 容器配置主机机器。

先决条件

  • 主机在 Red Hat Enterprise Linux 9.6 及更新版本中运行。
  • 已安装 tunedtuned-profiles-realtime 软件包。
  • tuned 服务正在运行。
  • podman 应用程序已安装并运行。

流程

  1. 安装所需的软件包:

    $ sudo dnf install rteval kernel-rt podman -y
    Copy to Clipboard
  2. 查看安装的内核:

    $ sudo grubby --info=ALL
    index=0
    kernel="/boot/vmlinuz-5.XX.0-XX.X.X.el9_6.x86_64+rt"
    args="ro crashkernel=2G-64G:256M,64G-:512M resume=UUID=3e14acf4-a359-4045-b8fc-990ff83743ec rd.lvm.lv=rhel_rt-qe-11/root rd.lvm.lv=rhel_rt-qe-11/swap console=ttyS0,115200n81 $tuned_params"
    root="/dev/mapper/rhel_rt--qe--11-root"
    initrd="/boot/initramfs-5.XX.0-XX.X.X.el9_6.x86_64+rt.img $tuned_initrd"
    title="Red Hat Enterprise Linux (5.XX.0-XX.X.X.el9_6.x86_64+rt) 9.6 (Plow)"
    id="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-5.XX.0-XX.X.X.el9_6.x86_64+rt"
    index=1
    kernel="/boot/vmlinuz-5.XX.0-XX.X.X.el9_6.x86_64"
    args="ro crashkernel=2G-64G:256M,64G-:512M resume=UUID=3e14acf4-a359-4045-b8fc-990ff83743ec rd.lvm.lv=rhel_rt-qe-11/root rd.lvm.lv=rhel_rt-qe-11/swap console=ttyS0,115200n81 $tuned_params"
    root="/dev/mapper/rhel_rt--qe--11-root"
    initrd="/boot/initramfs-5.XX.0-XX.X.X.el9_6.x86_64.img $tuned_initrd"
    title="Red Hat Enterprise Linux (5.XX.0-XX.X.X.el9_6.x86_64) 9.6 (Plow)"
    id="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-5.XX.0-XX.X.X.el9_6.x86_64"
    index=2
    kernel="/boot/vmlinuz-0-rescue-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
    args="ro crashkernel=2G-64G:256M,64G-:512M resume=UUID=3e14acf4-a359-4045-b8fc-990ff83743ec rd.lvm.lv=rhel_rt-qe-11/root rd.lvm.lv=rhel_rt-qe-11/swap console=ttyS0,115200n81"
    root="/dev/mapper/rhel_rt--qe--11-root"
    initrd="/boot/initramfs-0-rescue-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.img"
    title="Red Hat Enterprise Linux (0-rescue-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX) 9.6 (Plow)"
    id="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-0-rescue"
    Copy to Clipboard
  3. 将 Real Time 内核设置为默认内核:

    $ select a in /boot/vmlinuz-*rt*; do grubby --set-default=$a; break; done
    Copy to Clipboard
  4. 使用 tuned-adm 应用 realtime 配置集:

    $ sudo tuned-adm profile realtime
    Copy to Clipboard
  5. 重启主机机器:

    $ sudo reboot
    Copy to Clipboard

验证

  1. 验证内核版本和调优参数:

    $ sudo uname -r
    5.XX.0-XX.X.X.el9_6.x86_64+rt
    Copy to Clipboard
    $ sudo cat /proc/cmdline
    BOOT_IMAGE=(hd0,gpt2)/vmlinuz-5.XX.0-XX.X.X.el9_6.x86_64+rt root=/dev/mapper/rhel_rt--qe--11-root ro crashkernel=2G-64G:256M,64G-:512M resume=UUID=3e14acf4-a359-4045-b8fc-990ff83743ec rd.lvm.lv=rhel_rt-qe-11/root rd.lvm.lv=rhel_rt-qe-11/swap console=ttyS0,115200n81 skew_tick=1 tsc=reliable rcupdate.rcu_normal_after_boot=1 isolcpus=managed_irq,domain,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47 intel_pstate=disable nosoftlockup
    Copy to Clipboard

9.2. 为基准结果测试裸机

在裸机上运行系统,无需额外软件,但基本操作系统和 rteval 容器除外。这样可确保系统针对低延迟性能进行了优化,结果不会受到其他软件或进程的影响。

先决条件

流程

  • 运行 rteval 容器有两种场景:

    • 在单个 NUMA 节点上:

      $ rteval --duration 2h
      Copy to Clipboard
    • 在多个 NUMA 节点上运行 rteval 容器:

      $ rteval --duration 2h --loads-cpulist 0,1 --measurement-cpulist 2-47
      Copy to Clipboard

完成裸机测试后,使用 rteval 容器作为基准来配置应用程序和服务的实际场景。

9.3. 使用容器放置优化 CPU 性能

使用实时配置集调整主机后,您可以通过选择将容器放置到特定 CPU 并调整容器运行时行为来进一步优化性能。使用这些策略,您可以探索 CPU 隔离和 cgroup 配置如何影响容器化工作负载中的延迟。

9.3.1. 在所有 CPU 上运行 podman

要使用 rteval 容器运行 podman,请使用 tuned realtime 配置集或自定义系统调整来调优您的系统。确定您是否需要对您要测量的场景进行 CPU 隔离。确保正确设置 CPU 隔离以避免在某些情况下运行容器时出现问题。

检查 /proc/cmdline 中的 isolcpus= 参数。如果没有设置 isolcpus,您的系统不会隔离任何 CPU,您可以在所有 CPU 上运行容器。

先决条件

  • /proc/cmdline 中的 isolcpus= 参数设为在所有 CPU 之间运行容器。
  • 主机在 Red Hat Enterprise Linux 9.6 或更高版本中运行。
  • podman 服务正在运行。
  • rteval 容器已安装并运行。

流程

  1. 登录到 podman registry:

    $ podman login registry.redhat.io
    Copy to Clipboard
  2. 运行 rteval 容器。选择以下方法之一来运行容器:

    • 在单个 NUMA 节点框中的所有 CPU 上:

      $ podman run -it --rm --privileged --pids-limit=0 registry.redhat.io/rhel10/rteval \
      	/bin/bash -c 'rteval --duration 2h'
      Copy to Clipboard
    • 在多 NUMA 节点机器中:

      $ podman run -it --rm --privileged --pids-limit=0 registry.redhat.io/rhel10/rteval \
      	/bin/bash -c 'rteval --duration 2h --loads-cpulist 0,1 --measurement-cpulist 2-47
      Copy to Clipboard
      --pids-limit=0

      kcompile 可在不达到容器运行时的默认限制的情况下运行。

      kcompile 是一个命令行工具,用于为当前运行的内核编译内核模块,而无需重建整个内核。

      --privileged
      容器可以访问主机系统上的所有设备。这是 rteval 正确运行所必需的。

这些命令在所有可用节点上运行单个容器。tuned 服务管理主机性能优化,可让您在仅使用单个 CPU 时评估裸机性能。

验证

  • 在新终端中,列出所有包括 rteval 容器的容器,以确保它正确运行:

    $ podman ps -a
    Copy to Clipboard

9.3.2. 运行带有分割 CPU 分配的 podman

您可以将不同的容器分配给不同的 CPU 集,以测试负载分离和测量。例如,当只有一个 NUMA 节点且您想要将负载和测量分隔到容器中时,您可以运行两个不同的容器。在这种情况下,两个容器都在每个 CPU 上运行,且没有分区用于调整。

示例命令:

  • 加载容器

    $ podman run -it --rm --privileged --pids-limit=0 registry.redhat.io/rhel10/rteval \
       	/bin/bash -c 'rteval --duration 2h --onlyload'
    Copy to Clipboard
  • 测量容器

    $ podman run -it --rm --privileged --pids-limit=0 registry.redhat.io/rhel10/rteval \
       	/bin/bash -c 'rteval --duration 2h --onlymeasure'
    Copy to Clipboard

对于在有多个 NUMA 节点或手动分区机器的框上分区的情况,示例命令有:

  • 加载容器

    $ podman run -it --rm --privileged --pids-limit=0 --cpuset-cpus 0,1 registry.redhat.io/rhel10/rteval \
    /bin/bash -c 'rteval --duration 2h --onlyload --loads-cpulist 0,1'
    Copy to Clipboard
  • 测量容器

    $ podman run -it --rm --privileged --pids-limit=0 --cpuset-cpus 2-47 registry.redhat.io/rhel10/rteval \
    /bin/bash -c 'rteval --duration 2h --noload --measurement-cpulist 2-47'
    Copy to Clipboard

运行这些命令后,加载容器会在内务核上生成负载,而测量容器则在 isol_cpu 集上运行。

如果没有配置分区,则一个容器会在系统上的所有 CPU 之间生成负载,另一个容器会在所有节点上测量延迟。

在这两种情况下,负载和测量会在两个容器之间成功分开。

9.3.3. 在实时配置集中调整每个 NUMA 的内务操作

您可以在实时配置集中调整每个 NUMA 节点设置的内务 CPU。这通过确保内务务任务在 NUMA 节点之间平均分配,从而优化系统性能。

这对具有多个 NUMA 节点的系统特别有用,因为它有助于减少争用并改进整体性能。

默认 realtime tuned 配置集为每个 NUMA 节点保留一个内务 CPU (hk_per_numa=1)。如果需要更多可用于容器工作负载的 CPU,您可以修改此行为。

先决条件

  • 主机在 Red Hat Enterprise Linux 9.6 或更高版本中运行。
  • tuned 服务正在运行。
  • rteval 容器已安装并运行。
  • podman 服务正在运行。
  • 已安装 tuned-profiles-realtime 软件包。

流程

  1. 修改 realtime-variables.conf 文件,以调整每个 NUMA 节点设置的内务 CPU。

    • 在文本编辑器中打开位于 /etc/tunedrealtime-variables.conf 文件:

      $ sudo vi /etc/tuned/realtime-variables.conf
      Copy to Clipboard
    • 找到 isolated_cores 变量。默认情况下,这设置为 1,这意味着每个 NUMA 节点保留了一个内核,用于隔离或非内部维护。您可以增加这个值,但它必须小于每个 NUMA 节点的 CPU 总数。

      以下示例将每个 NUMA 节点有 24 个内核的系统中将 isolated_cores 设置为 3

      isolated_cores=${f:calc_isolated_cores:3}
      Copy to Clipboard
  2. 保存您的更改并关闭该文件。
  3. 重新应用 tuned 实时配置集:

    $ sudo tuned-adm profile realtime
    Copy to Clipboard

这会导致测试过程中产生 6 个 CPU (每个 NUMA 节点 3)生成负载,而系统则保留了 isolcpus 集的剩余内核。此配置用于测量。在某些情况下,混合优先级配置可能会在自定义拓扑上部署容器,而不是在设置 isolcpus 上。

或者,您可以手动指定自定义 CPU 范围,而不依赖于每个节点的自动计数。这样可确保对隔离内核的完整控制,从而更易于使用非统一拓扑或专用 CPU 布局调整系统。

验证

  1. 验证 realtime-variables.conf 文件中的更改。
  2. 重启系统以应用更改。
  3. 查看 /proc/cmdline 文件,以确认 isolcpus 设置:

    $ cat /proc/cmdline
    BOOT_IMAGE=(hd0,gpt2)/vmlinuz-5.XX.X-XX.X.X.el9_6.x86_64+rt root=/dev/mapper/rhel_rt--qe--11-root ro  crashkernel=1G-4G:192M,4G-64G:256M,64G-:512M resume=UUID=00cbf36d-ffaa-4285-a381-5c1d868eb3e3 rd.lvm.lv=rhel_rt-qe-11/root rd.lvm.lv=rhel_rt-qe-11/swap console=ttyS0,115200n81 skew_tick=1 tsc=reliable rcupdate.rcu_normal_after_boot=1 isolcpus=managed_irq,domain,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47 intel_pstate=disable nosoftlockup
    Copy to Clipboard

9.3.4. 在隔离的 CPU 之间分散多个容器

要在隔离的 CPU 之间运行多个容器,您可以使用 --cpuset-cpus 选项指定每个容器应使用哪些 CPU。这会划分多个隔离的 CPU 的负载,从而提高性能并减少争用。

您可以将设置在多个容器间划分的 isolcpus 模拟以下任务:

  • 并发对延迟敏感的任务。
  • 跨分区系统进行多个负载。
9.3.4.1. 模拟并发对延迟敏感的任务

要模拟并发对延迟敏感的任务,您可以将特定的隔离 CPU 分配给每个容器。以下示例演示了如何在不同的 CPU 集间配置和运行容器。

在 CPU 0-6 上运行一个容器,在 CPU 7-28 上运行另一个容器,在 CPU 29-47 上运行第三个容器。使用以下命令:

$ podman run -it --rm --privileged --pids-limit=0 --cpuset-cpus 0-6 registry.redhat.io/rhel10/rteval \
    /bin/bash -c 'rteval --duration 2h --onlyload --loads-cpulist 0-6'
Copy to Clipboard
$ podman run -it --rm --privileged --pids-limit=0 --cpuset-cpus 7-28 registry.redhat.io/rhel10/rteval \
    /bin/bash -c 'rteval --duration 2h --onlyload --loads-cpulist 7-28'
Copy to Clipboard
$ podman run -it --rm --privileged --pids-limit=0 --cpuset-cpus 29-47 registry.redhat.io/rhel10/rteval \
    /bin/bash -c 'rteval --duration 2h --onlyload --loads-cpulist 29-47'
Copy to Clipboard
9.3.4.2. 在分区系统中模拟多个负载

在非隔离 CPU 集上启动 rteval 负载生成器。接下来,在 isolcpus 设置的一部分上模拟高吞吐量的应用,如高速数据库容器。在本例中,CPU 7-28 用于代表高速数据库容器。在单独的终端会话中运行以下命令以启动负载。

$ podman run -it --rm --privileged --pids-limit=0 --cpuset-cpus 0-6 registry.redhat.io/rhel10/rteval \
    /bin/bash -c 'rteval --duration 2h --onlyload --loads-cpulist 0-6'
Copy to Clipboard

然后,在一个单独的终端中,在隔离 CPU 的子集上生成一些负载:

$ podman run -it --rm --privileged --pids-limit=0 --cpuset-cpus 20-30 registry.redhat.io/rhel10/rteval \
    /bin/bash -c 'rteval --duration 2h --onlyload --loads-cpulist 20-30'
Copy to Clipboard

现在,要在剩余的 CPU 上运行测量线程,有两个选项。您可以将隔离 CPU 的两个剩余子集部署到单独的容器,或者运行使用两个剩余的 CPU 子集的单个测量容器。

  • 选项 1:部署两个测量容器

    $ podman run -it --rm --privileged --pids-limit=0 --cpuset-cpus 7-19 registry.redhat.io/rhel10/rteval \
        /bin/bash -c 'rteval --duration 2h --noload --measurement-cpulist 7-19'
    Copy to Clipboard
    $ podman run -it --rm --privileged --pids-limit=0 --cpuset-cpus 31-47 registry.redhat.io/rhel10/rteval \
        /bin/bash -c 'rteval --duration 2h --noload --measurement-cpulist 31-47'
    Copy to Clipboard
  • 选项 2:部署单个测量容器

    $ podman run -it --rm --privileged --pids-limit=0 --cpuset-cpus 7-19,31-47 registry.redhat.io/rhel10/rteval \
        /bin/bash -c 'rteval --duration 2h --noload --measurement-cpulist 7-19,31-47'
    Copy to Clipboard

第 10 章 使用 cgroupfs 手动管理 cgroup

您可以通过在 cgroupfs 虚拟文件系统中创建目录来管理系统上的 cgroup 层次结构。文件系统默认挂载到 /sys/fs/cgroup/ 目录中,您可以在专用控制文件中指定所需的配置。

重要

通常,红帽建议您使用 systemd 来控制系统资源的使用。您应该只在特殊情况下手动配置 cgroups 虚拟文件系统。例如,当您需要使用在 cgroup-v2 层次结构中没有对应的 cgroup-v1 控制器时。

10.1. 在 cgroups-v2 文件系统中创建 cgroup 和启用控制器

您可以通过创建和删除目录,并通过写入 cgroup 虚拟文件系统中的文件来管理 控制组 (cgroups)。文件系统默认挂载到 /sys/fs/cgroup/ 目录中。要使用 cgroups 控制器中的设置,您还需要为子 cgroup 启用所需的控制器。在默认情况下,root cgroup 会为其子 cgroups 启用 memorypids。因此,您必须在 /sys/fs/ cgroup / root cgroup 中创建至少两个级别的子 cgroup。这样,您可以选择从子 cgroup 中删除 memorypids 控制器,并更好地组织 cgroup 文件。

先决条件

  • 您在系统上具有 root 权限。

流程

  1. 创建 /sys/fs/cgroup/Example/ 目录:

    # mkdir /sys/fs/cgroup/Example/
    Copy to Clipboard

    /sys/fs/cgroup/Example/ 目录定义一个子组。当您创建 /sys/fs/cgroup/Example/ 目录时,目录中会自动创建一些 cgroups-v2 接口文件。/sys/fs/cgroup/Example/ 目录还包含 memorypids 控制器的特定于控制器的文件。

  2. 可选:检查新创建的子控制组:

    # ll /sys/fs/cgroup/Example/
    -r—​r—​r--. 1 root root 0 Jun  1 10:33 cgroup.controllers
    -r—​r—​r--. 1 root root 0 Jun  1 10:33 cgroup.events
    -rw-r—​r--. 1 root root 0 Jun  1 10:33 cgroup.freeze
    -rw-r--​r--. 1 root root 0 Jun  1 10:33 cgroup.procs
    …​
    -rw-r—​r--. 1 root root 0 Jun  1 10:33 cgroup.subtree_control
    -r—​r—​r--. 1 root root 0 Jun  1 10:33 memory.events.local
    -rw-r—​r--. 1 root root 0 Jun  1 10:33 memory.high
    -rw-r—​r--. 1 root root 0 Jun  1 10:33 memory.low
    …​
    -r—​r—​r--. 1 root root 0 Jun  1 10:33 pids.current
    -r—​r—​r--. 1 root root 0 Jun  1 10:33 pids.events
    -rw-r—​r--. 1 root root 0 Jun  1 10:33 pids.max
    Copy to Clipboard

    示例输出显示常规 cgroup 控制接口文件,如 cgroup.procscgroup.controllers。无论启用控制器是什么,这些文件都是所有控制组通用的。

    memory.highpids.max 等文件与 memorypids 控制器有关,它们是 root 控制组 (/sys/fs/cgroup/) ,默认情况下会被 systemd 启用。

    默认情况下,新创建的子组从父 cgroup 继承所有设置。在这种情况下,来自 root cgroup 没有限制。

  3. 验证 /sys/fs/cgroup/cgroup.controllers 文件中是否有所需的控制器:

    # cat /sys/fs/cgroup/cgroup.controllers
    cpuset cpu io memory hugetlb pids rdma
    Copy to Clipboard
  4. 启用所需的控制器。在本例中是 cpucpuset 控制器:

    # echo "+cpu" >> /sys/fs/cgroup/cgroup.subtree_control
    # echo "+cpuset" >> /sys/fs/cgroup/cgroup.subtree_control
    Copy to Clipboard

    这些命令为 /sys/fs/cgroup/ root 控制组的直接子组启用 cpucpuset 控制器。包含新创建的 Example 控制组。子组 是可以指定进程,并根据标准对每个进程应用控制检查的位置。

    用户可以在任意级别读取 cgroup.subtree_control 文件的内容,以了解即时子组中哪些控制器可用于启用。

    注意

    默认情况下,根控制组中的 /sys/fs/cgroup/cgroup.subtree_control 文件包含 memorypids 控制器。

  5. Example 控制组群的子 cgroup 启用所需的控制器:

    # echo "+cpu +cpuset" >> /sys/fs/cgroup/Example/cgroup.subtree_control
    Copy to Clipboard

    这些命令可确保,直接的子组具有与 CPU 时间分发相关的控制器,而不是 memorypids 控制器。

  6. 创建 /sys/fs/cgroup/Example/tasks/ 目录:

    # mkdir /sys/fs/cgroup/Example/tasks/
    Copy to Clipboard

    /sys/fs/cgroup/Example/tasks/ 目录定义了一个子组,它带有只与 cpucpuset 控制器相关的文件。现在,您可以将进程分配到此控制组,并将 cpucpuset 控制器选项用于您的进程。

  7. 可选:检查子控制组:

    # ll /sys/fs/cgroup/Example/tasks
    -r—​r—​r--. 1 root root 0 Jun  1 11:45 cgroup.controllers
    -r—​r—​r--. 1 root root 0 Jun  1 11:45 cgroup.events
    -rw-r—​r--. 1 root root 0 Jun  1 11:45 cgroup.freeze
    -rw-r—​r--. 1 root root 0 Jun  1 11:45 cgroup.max.depth
    -rw-r—​r--. 1 root root 0 Jun  1 11:45 cgroup.max.descendants
    -rw-r—​r--. 1 root root 0 Jun  1 11:45 cgroup.procs
    -r—​r—​r--. 1 root root 0 Jun  1 11:45 cgroup.stat
    -rw-r—​r--. 1 root root 0 Jun  1 11:45 cgroup.subtree_control
    -rw-r—​r--. 1 root root 0 Jun  1 11:45 cgroup.threads
    -rw-r—​r--. 1 root root 0 Jun  1 11:45 cgroup.type
    -rw-r—​r--. 1 root root 0 Jun  1 11:45 cpu.max
    -rw-r—​r--. 1 root root 0 Jun  1 11:45 cpu.pressure
    -rw-r—​r--. 1 root root 0 Jun  1 11:45 cpuset.cpus
    -r—​r—​r--. 1 root root 0 Jun  1 11:45 cpuset.cpus.effective
    -rw-r—​r--. 1 root root 0 Jun  1 11:45 cpuset.cpus.partition
    -rw-r—​r--. 1 root root 0 Jun  1 11:45 cpuset.mems
    -r—​r—​r--. 1 root root 0 Jun  1 11:45 cpuset.mems.effective
    -r—​r—​r--. 1 root root 0 Jun  1 11:45 cpu.stat
    -rw-r—​r--. 1 root root 0 Jun  1 11:45 cpu.weight
    -rw-r—​r--. 1 root root 0 Jun  1 11:45 cpu.weight.nice
    -rw-r—​r--. 1 root root 0 Jun  1 11:45 io.pressure
    -rw-r—​r--. 1 root root 0 Jun  1 11:45 memory.pressure
    Copy to Clipboard
重要

cpu 控制器只有在相关子控制组至少有 2 个在单个 CPU 上竞争时间的进程时,才会被激活 。

验证

  • 可选:确认您是否已创建了一个只有所需的控制器处于活跃状态的新的 cgroup

    # cat /sys/fs/cgroup/Example/tasks/cgroup.controllers
    cpuset cpu
    Copy to Clipboard

10.2. 通过调整 CPU 权重来控制应用程序的 CPU 时间分布

您需要为 cpu 控制器的相关文件分配值,以控制分发到特定 cgroup 树下应用程序的 CPU 时间。

先决条件

  • 您在系统上具有 root 权限。
  • 您有要控制 CPU 时间分布的应用程序。
  • 您已挂载了 cgroups-v2 文件系统。
  • 您在 /sys/fs/cgroup/ 根控制组 中创建了两级 子控制组,如下例所示:

    …​
      ├── Example
      │   ├── g1
      │   ├── g2
      │   └── g3
    …​
    Copy to Clipboard
  • 您已在父控制组和子控制组中启用 cpu 控制器,类似于在 cgroups-v2 文件系统中创建 cgroups 并启用控制器

流程

  1. 配置所需的 CPU 权重,以便在控制组内实现资源限制:

    # echo "150" > /sys/fs/cgroup/Example/g1/cpu.weight
    # echo "100" > /sys/fs/cgroup/Example/g2/cpu.weight
    # echo "50" > /sys/fs/cgroup/Example/g3/cpu.weight
    Copy to Clipboard
  2. 将应用程序的 PID 添加到 g1g2g3 子组中:

    # echo "33373" > /sys/fs/cgroup/Example/g1/cgroup.procs
    # echo "33374" > /sys/fs/cgroup/Example/g2/cgroup.procs
    # echo "33377" > /sys/fs/cgroup/Example/g3/cgroup.procs
    Copy to Clipboard

    这些命令确保所需的应用程序成为 Example/g*/ 子 cgroup 的成员,并根据这些 cgroup 的配置获得其分配的 CPU 时间。

    已运行进程的子 cgroup (g1, g2, g3) 的权重在父 cgroup(Example)级别上相加。然后根据分配的权重按比例分配 CPU 资源。

    因此,当所有进程在同一时间运行时,内核会根据分配的 cgroup 的 cpu.weight 文件,为每个进程分配相应的 CPU 时间:

    子 cgroupcpu.weight 文件CPU 时间分配

    g1

    150

    ~50% (150/300)

    g2

    100

    ~33% (100/300)

    g3

    50

    ~16% (50/300)

    cpu.weight 控制器文件的值不是一个百分比。

    如果一个进程停止运行,使 cgroup g2 没有运行进程,则计算将省略 cgroup g2,仅计算 cgroup g1g3 的帐户权重:

    子 cgroupcpu.weight 文件CPU 时间分配

    g1

    150

    ~75% (150/200)

    g3

    50

    ~25% (50/200)

    重要

    如果子 cgroup 有多个正在运行的进程,则分配给 cgroup 的 CPU 时间在其成员进程中平均分配。

验证

  1. 验证应用程序是否运行在指定的控制组中:

    # cat /proc/33373/cgroup /proc/33374/cgroup /proc/33377/cgroup
    0::/Example/g1
    0::/Example/g2
    0::/Example/g3
    Copy to Clipboard

    命令输出显示了运行在 Example/g*/ 子 cgroup 中指定的应用程序的进程。

  2. 检查节流应用程序的当前 CPU 消耗:

    # top
    top - 05:17:18 up 1 day, 18:25,  1 user,  load average: 3.03, 3.03, 3.00
    Tasks:  95 total,   4 running,  91 sleeping,   0 stopped,   0 zombie
    %Cpu(s): 18.1 us, 81.6 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.3 hi,  0.0 si,  0.0 st
    MiB Mem :   3737.0 total,   3233.7 free,    132.8 used,    370.5 buff/cache
    MiB Swap:   4060.0 total,   4060.0 free,      0.0 used.   3373.1 avail Mem
    
        PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
      33373 root      20   0   18720   1748   1460 R  49.5   0.0 415:05.87 sha1sum
      33374 root      20   0   18720   1756   1464 R  32.9   0.0 412:58.33 sha1sum
      33377 root      20   0   18720   1860   1568 R  16.3   0.0 411:03.12 sha1sum
        760 root      20   0  416620  28540  15296 S   0.3   0.7   0:10.23 tuned
          1 root      20   0  186328  14108   9484 S   0.0   0.4   0:02.00 systemd
          2 root      20   0       0      0      0 S   0.0   0.0   0:00.01 kthread
    ...
    Copy to Clipboard
    注意

    为了清晰地说明,所有进程都在一个 CPU 上运行。当在多个 CPU 上使用时,CPU 权重会应用同样的原则。

    请注意,PID 33373PID 33374PID 33377 的 CPU 资源是根据您分配给子 cgroup 的 150、100 和 50 权重分配的。权重对应于每个应用程序分配的 CPU 时间的大约 50%、33% 和 16%。

10.3. 挂载 cgroups-v1

在启动过程中,RHEL 10 默认挂载 cgroup-v2 虚拟文件系统。要在限制应用程序的资源中使用 cgroup-v1 功能,请手动配置系统。

注意

内核中完全启用了 cgroup-v1cgroup-v2。从内核的角度来看,没有默认的控制组版本,并且由 systemd 决定在启动时挂载。

先决条件

  • 您在系统上具有 root 权限。

流程

  1. 将系统配置为,在系统引导过程中,默认由 systemd 系统和服务管理器挂载 cgroups-v1

    # grubby --update-kernel=/boot/vmlinuz-$(uname -r) --args="systemd.unified_cgroup_hierarchy=0 systemd.legacy_systemd_cgroup_controller"
    Copy to Clipboard

    这会在当前引导条目中添加所需的内核命令行参数。

    在所有内核引导条目中添加相同的参数:

    # grubby --update-kernel=ALL --args="systemd.unified_cgroup_hierarchy=0 systemd.legacy_systemd_cgroup_controller"
    Copy to Clipboard
  2. 重启系统以使更改生效。

验证

  1. 验证 cgroups-v1 文件系统是否已挂载:

    # mount -l | grep cgroup
    tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,seclabel,size=4096k,nr_inodes=1024,mode=755,inode64)
    cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
    cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,perf_event)
    cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,cpu,cpuacct)
    cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,pids)
    cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,cpuset)
    cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,net_cls,net_prio)
    cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,hugetlb)
    cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,memory)
    cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,blkio)
    cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,devices)
    cgroup on /sys/fs/cgroup/misc type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,misc)
    cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,freezer)
    cgroup on /sys/fs/cgroup/rdma type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,rdma)
    Copy to Clipboard

    与各种 cgroup-v1 控制器对应的 cgroups-v1 文件系统已被成功挂载到 /sys/fs/cgroup/ 目录中。

  2. 检查 /sys/fs/cgroup/ 目录的内容:

    # ll /sys/fs/cgroup/
    dr-xr-xr-x. 10 root root  0 Mar 16 09:34 blkio
    lrwxrwxrwx.  1 root root 11 Mar 16 09:34 cpu → cpu,cpuacct
    lrwxrwxrwx.  1 root root 11 Mar 16 09:34 cpuacct → cpu,cpuacct
    dr-xr-xr-x. 10 root root  0 Mar 16 09:34 cpu,cpuacct
    dr-xr-xr-x.  2 root root  0 Mar 16 09:34 cpuset
    dr-xr-xr-x. 10 root root  0 Mar 16 09:34 devices
    dr-xr-xr-x.  2 root root  0 Mar 16 09:34 freezer
    dr-xr-xr-x.  2 root root  0 Mar 16 09:34 hugetlb
    dr-xr-xr-x. 10 root root  0 Mar 16 09:34 memory
    dr-xr-xr-x.  2 root root  0 Mar 16 09:34 misc
    lrwxrwxrwx.  1 root root 16 Mar 16 09:34 net_cls → net_cls,net_prio
    dr-xr-xr-x.  2 root root  0 Mar 16 09:34 net_cls,net_prio
    lrwxrwxrwx.  1 root root 16 Mar 16 09:34 net_prio → net_cls,net_prio
    dr-xr-xr-x.  2 root root  0 Mar 16 09:34 perf_event
    dr-xr-xr-x. 10 root root  0 Mar 16 09:34 pids
    dr-xr-xr-x.  2 root root  0 Mar 16 09:34 rdma
    dr-xr-xr-x. 11 root root  0 Mar 16 09:34 systemd
    Copy to Clipboard

    默认情况下,/sys/fs/cgroup/ 目录(也称为 root 控制组 )包含特定于控制器的目录,如 cpuset。另外,还有一些与 systemd 相关的目录。

10.4. 使用 cgroups-v1 为应用程序设置 CPU 限制

要使用 控制组版本 1 ( cgroups-v1)配置对应用程序的 CPU 限制,请使用 /sys/fs/ 虚拟文件系统。

先决条件

  • 您在系统上具有 root 权限。
  • 您有一个安装在系统上的限制其 CPU 消耗的应用程序。
  • 您将系统配置为,在系统引导过程中,默认由 systemd 系统和服务管理器挂载 cgroups-v1

    # grubby --update-kernel=/boot/vmlinuz-$(uname -r) --args="systemd.unified_cgroup_hierarchy=0 systemd.legacy_systemd_cgroup_controller"
    Copy to Clipboard

    这会在当前引导条目中添加所需的内核命令行参数。

流程

  1. 识别您要限制 CPU 消耗的应用程序的进程 ID (PID):

    # top
    top - 11:34:09 up 11 min,  1 user,  load average: 0.51, 0.27, 0.22
    Tasks: 267 total,   3 running, 264 sleeping,   0 stopped,   0 zombie
    %Cpu(s): 49.0 us,  3.3 sy,  0.0 ni, 47.5 id,  0.0 wa,  0.2 hi,  0.0 si,  0.0 st
    MiB Mem :   1826.8 total,    303.4 free,   1046.8 used,    476.5 buff/cache
    MiB Swap:   1536.0 total,   1396.0 free,    140.0 used.    616.4 avail Mem
    
      PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
     6955 root      20   0  228440   1752   1472 R  99.3   0.1   0:32.71 sha1sum
     5760 jdoe      20   0 3603868 205188  64196 S   3.7  11.0   0:17.19 gnome-shell
     6448 jdoe      20   0  743648  30640  19488 S   0.7   1.6   0:02.73 gnome-terminal-
        1 root      20   0  245300   6568   4116 S   0.3   0.4   0:01.87 systemd
      505 root      20   0       0      0      0 I   0.3   0.0   0:00.75 kworker/u4:4-events_unbound
    ...
    Copy to Clipboard

    PID 6955sha1sum 示例应用程序消耗大量的 CPU 资源。

  2. cpu 资源控制器目录中创建子目录:

    # mkdir /sys/fs/cgroup/cpu/Example/
    Copy to Clipboard

    此目录代表控制组,您可以在其中放置特定进程,并向进程应用某些 CPU 限制。同时,目录中将创建多个 cgroups-v1 接口文件和 cpu 特定于控制器的文件。

  3. 可选:检查新创建的控制组:

    # ll /sys/fs/cgroup/cpu/Example/
    -rw-r—​r--. 1 root root 0 Mar 11 11:42 cgroup.clone_children
    -rw-r—​r--. 1 root root 0 Mar 11 11:42 cgroup.procs
    -r—​r—​r--. 1 root root 0 Mar 11 11:42 cpuacct.stat
    -rw-r—​r--. 1 root root 0 Mar 11 11:42 cpuacct.usage
    -r—​r—​r--. 1 root root 0 Mar 11 11:42 cpuacct.usage_all
    -r—​r—​r--. 1 root root 0 Mar 11 11:42 cpuacct.usage_percpu
    -r—​r—​r--. 1 root root 0 Mar 11 11:42 cpuacct.usage_percpu_sys
    -r—​r—​r--. 1 root root 0 Mar 11 11:42 cpuacct.usage_percpu_user
    -r—​r—​r--. 1 root root 0 Mar 11 11:42 cpuacct.usage_sys
    -r—​r—​r--. 1 root root 0 Mar 11 11:42 cpuacct.usage_user
    -rw-r—​r--. 1 root root 0 Mar 11 11:42 cpu.cfs_period_us
    -rw-r—​r--. 1 root root 0 Mar 11 11:42 cpu.cfs_quota_us
    -rw-r—​r--. 1 root root 0 Mar 11 11:42 cpu.rt_period_us
    -rw-r—​r--. 1 root root 0 Mar 11 11:42 cpu.rt_runtime_us
    -rw-r—​r--. 1 root root 0 Mar 11 11:42 cpu.shares
    -r—​r—​r--. 1 root root 0 Mar 11 11:42 cpu.stat
    -rw-r—​r--. 1 root root 0 Mar 11 11:42 notify_on_release
    -rw-r—​r--. 1 root root 0 Mar 11 11:42 tasks
    Copy to Clipboard

    cpuacct.usage,cpu.cfs._period_us 等文件代表特定的配置和/或限制,可以为 Example 控制组中的进程设置它们。请注意,文件名是以它们所属的控制组控制器的名称为前缀的。

    默认情况下,新创建的控制组继承对系统整个 CPU 资源的访问权限,且无限制。

  4. 为控制组群配置 CPU 限制:

    # echo "1000000" > /sys/fs/cgroup/cpu/Example/cpu.cfs_period_us
    # echo "200000" > /sys/fs/cgroup/cpu/Example/cpu.cfs_quota_us
    Copy to Clipboard
    • cpu.cfs_period_us 文件代表控制组对 CPU 资源的访问多久需要重新分配一次。时间段为微秒(µs, "us")。上限为 1000 000 微秒,下限为 1000 微秒。
    • cpu.cfs_quota_us 文件表示控制组中的所有进程在一段时间内共同运行的总时间(以微秒为单位),如 cpu.cfs_period_us 所定义的那样。当控制组中的进程在单个时间段内用完配额指定的所有时间时,它们在该时段的剩余时间内受到限制,并且不允许运行,直到下一个时间段为止。下限为 1000 微秒。

      上面的示例命令设定 CPU 时间限值,使得 Example 控制组中的所有进程仅能每 1 秒( cpu.cfs_quota_us 定义)每 1 秒(由 cpu.cfs_period_us 定义)运行 0.2 秒。

  5. 可选:验证限制:

    # cat /sys/fs/cgroup/cpu/Example/cpu.cfs_period_us /sys/fs/cgroup/cpu/Example/cpu.cfs_quota_us
    1000000
    200000
    Copy to Clipboard
  6. 将应用程序的 PID 添加到 Example 控制组群中:

    # echo "6955" > /sys/fs/cgroup/cpu/Example/cgroup.procs
    Copy to Clipboard

    此命令可确保特定的应用成为 Example 控制组的一员,并且不超过为 Example 控制组配置的 CPU 限制。PID 必须代表系统中一个存在的进程。此处的 PID 6955 被分配给 sha1sum /dev/zero & 进程,用来演示 cpu 控制器的用例。

验证

  1. 验证应用程序是否在指定的控制组群中运行:

    # cat /proc/6955/cgroup
    12:cpuset:/
    11:hugetlb:/
    10:net_cls,net_prio:/
    9:memory:/user.slice/user-1000.slice/user@1000.service
    8:devices:/user.slice
    7:blkio:/
    6:freezer:/
    5:rdma:/
    4:pids:/user.slice/user-1000.slice/user@1000.service
    3:perf_event:/
    2:cpu,cpuacct:/Example
    1:name=systemd:/user.slice/user-1000.slice/user@1000.service/gnome-terminal-server.service
    Copy to Clipboard

    应用程序的进程在 Example 控制组中运行,后者将 CPU 限制应用到应用程序的进程。

  2. 确定节流应用程序的当前 CPU 消耗:

    # top
    top - 12:28:42 up  1:06,  1 user,  load average: 1.02, 1.02, 1.00
    Tasks: 266 total,   6 running, 260 sleeping,   0 stopped,   0 zombie
    %Cpu(s): 11.0 us,  1.2 sy,  0.0 ni, 87.5 id,  0.0 wa,  0.2 hi,  0.0 si,  0.2 st
    MiB Mem :   1826.8 total,    287.1 free,   1054.4 used,    485.3 buff/cache
    MiB Swap:   1536.0 total,   1396.7 free,    139.2 used.    608.3 avail Mem
    
      PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
     6955 root      20   0  228440   1752   1472 R  20.6   0.1  47:11.43 sha1sum
     5760 jdoe      20   0 3604956 208832  65316 R   2.3  11.2   0:43.50 gnome-shell
     6448 jdoe      20   0  743836  31736  19488 S   0.7   1.7   0:08.25 gnome-terminal-
      505 root      20   0       0      0      0 I   0.3   0.0   0:03.39 kworker/u4:4-events_unbound
     4217 root      20   0   74192   1612   1320 S   0.3   0.1   0:01.19 spice-vdagentd
    ...
    Copy to Clipboard

    请注意,PID 6955 的 CPU 消耗从 99% 减少到 20%。

注意

cpu.cfs_period_uscpu.cfs_quota_uscgroups-v2对应项是 cpu.max 文件。cpu.max 文件可以通过 cpu 控制器获得。

使用控制组(cgroups)内核功能,您可以控制应用程序的资源使用情况来更有效地使用它们。

您可以为以下任务使用 cgroups

  • 为系统资源分配设置限制。
  • 将硬件资源优先分配给特定的进程。
  • 防止某些进程获取硬件资源。

10.5. 控制组简介

使用 控制组 Linux 内核功能,您可以将进程组织为按层排序的组 - cgroups。您可以通过为 cgroup 虚拟文件系统提供结构来定义层次结构(控制组树),默认挂载到 /sys/fs/cgroup/ 目录。

systemd 服务管理器使用 cgroups 来组织它管理的所有单元和服务。您可以通过创建和删除 /sys/fs/cgroup/ 目录中的子目录来手动管理 cgroups 的层次结构。

然后,内核中的资源控制器通过限制、优先处理或分配这些进程的系统资源来在 cgroups 中修改进程的行为。这些资源包括以下内容:

  • CPU 时间
  • 内存
  • 网络带宽
  • 这些资源的组合

cgroups 的主要用例是聚合系统进程,并在应用程序和用户之间划分硬件资源。这样可以提高环境的效率、稳定性和安全性。

控制组群版本 1

控制组版本 1 (cgroups-v1) 提供按资源控制器层次结构。每个资源(如 CPU、内存或 I/O)都有自己的控制组层次结构。您可以组合不同的控制组层次结构,使一个控制器可以与另一个控制器协调管理各自的资源。但是,当两个控制器属于不同的进程层次结构时,协调受到限制。

cgroups-v1 控制器的开发时间跨度很大,导致其控制文件的行为和命名不一致。

控制组群版本 2

控制组版本 2 (cgroups-v2)提供单一控制组层次结构,用于挂载所有资源控制器。

控制文件行为和命名在不同控制器之间保持一致。

10.6. 内核资源控制器简介

内核资源控制器启用控制组的功能。RHEL 10 支持用于 控制组版本 1 (cgroups-v1)和 控制组版本 2 (cgroups-v2)的各种控制器。

资源控制器也称为控制组子系统,是一个代表单一资源的内核子系统,如 CPU 时间、内存、网络带宽或磁盘 I/O。Linux 内核提供由 systemd 服务管理器自动挂载的一系列资源控制器。

您可以在 /proc/cgroups 文件中找到当前挂载的资源控制器的列表。

cgroups-v1 提供的控制器:

blkio
设置对块设备的输入/输出访问的限制。
cpu
调整控制组任务的默认调度程序的参数。cpu 控制器与 cpuacct 控制器一起挂载在同一挂载上。
cpuacct
创建控制组群中任务所使用的有关 CPU 资源的自动报告。cpuacct 控制器与 cpu 控制器一起挂载在同一挂载上。
cpuset
将控制组任务限制为仅在指定 CPU 子集上运行,并指示任务仅使用指定内存节点上的内存。
devices
控制控制组群中任务对设备的访问。
freezer
暂停或恢复控制组中的任务。
内存
设置控制组中任务对内存使用的限制,并对这些任务使用的内存资源生成自动报告。
net_cls
使用类标识符(classid)标记网络数据包,使 Linux 流量控制器( tc 命令)能够识别来自特定控制组任务的数据包。net_cls 子系统 net_filter (iptables) 也可使用此标签对此类数据包执行操作。net_filter 使用防火墙标识符(fwid)标记网络套接字,它允许 Linux 防火墙识别来自特定控制组任务的数据包(通过使用 iptables 命令)。
net_prio
设置网络流量的优先级。
pids
为控制组群中的多个进程及其子进程设置限制。
perf_event
通过 perf 性能监控和报告工具对监控的任务进行分组。
rdma
对控制组群中远程直接内存访问/InfiniBand 特定资源设置限制。
hugetlb
按控制组群中的任务限制大型虚拟内存页的使用。

cgroups-v2 提供的控制器:

io
设置对块设备的输入/输出访问的限制。
内存
设置控制组中任务对内存使用的限制,并对这些任务使用的内存资源生成自动报告。
pids
为控制组群中的多个进程及其子进程设置限制。
rdma
对控制组群中远程直接内存访问/InfiniBand 特定资源设置限制。
cpu
调整控制组任务的默认调度程序的参数,并创建对控制组中任务所使用的 CPU 资源的自动报告。
cpuset
将控制组任务限制为仅在指定 CPU 子集上运行,并指示任务仅使用指定内存节点上的内存。仅支持具有新分区功能的核心功能(cpus{,.effective}, mems{,.effective})。
perf_event
通过 perf 性能监控和报告工具对监控的任务进行分组。perf_event 在 v2 层次结构上自动启用。
重要

资源控制器可以在 cgroups-v1 层次结构或 cgroups-v2 层次结构中使用,不能同时在两者中使用。

10.7. 命名空间简介

命名空间为组织和识别软件对象创建单独的空间。这使得它们不会互相影响。因此,每个软件对象都包含其自己的一组资源,如挂载点、网络设备或主机名,即使它们共享同样的系统。

使用命名空间的最常见技术是容器。

对特定全局资源的更改仅对该命名空间中的进程可见,不影响系统或其他命名空间的其余部分。

要检查进程所属的命名空间,您可以在 /proc/<PID>/ns/ 目录中检查符号链接。

表 10.1. 支持的命名空间以及它们隔离的资源:
NamespaceIsolates

Mount

挂载点

UTS

主机名和 NIS 域名

IPC

系统 V IPC, POSIX 消息队列

PID

进程 ID

Network

网络设备、堆栈、端口等

User

用户和组群 ID

Control groups

控制组群根目录

第 11 章 在 RHEL for Real Time 上设置 CPU 关联性

系统中的所有线程和中断源都有一个处理器关联性属性。操作系统调度程序使用此信息来确定要在 CPU 上运行的线程和中断。通过设置处理器关联性以及有效的策略和优先级设置,您可以实现最大可能的性能。应用程序始终与其他进程竞争资源(特别是 CPU 时间)。根据应用程序,相关的线程通常在同一核心上运行。或者,可以将一个应用程序线程分配给一个内核。

执行多任务的系统容易容易出错。当较低优先级的应用程序位于代码的关键部分时,即使高优先级的应用程序也会延迟执行。在低优先级应用程序退出 critical 部分后,内核可以安全地抢占低优先级应用程序,并在处理器上调度高优先级应用程序。另外,因为缓存无效,将进程从一个 CPU 迁移到另一个 CPU 可能会昂贵。RHEL for Real Time 包括解决其中一些问题的工具,并允许更好地控制延迟。

关联性表示为位掩码,掩码中的每个位代表一个 CPU 内核。如果位设置为 1,则线程或中断在该内核上运行;如果 0,则线程或中断将排除在核心上运行。关联性位掩码的默认值为 all,这意味着线程或中断可以在系统的任何核心上运行。

默认情况下,进程可以在任何 CPU 上运行。但是,通过更改进程的关联性,您可以定义要在预先确定的 CPU 集合上运行的进程。子进程继承其父进程的 CPU 相关性。

设置以下典型的关联性设置可达到最大可能的性能:

  • 将单个 CPU 内核用于所有系统进程,并将应用程序设置为在内核的其余部分上运行。
  • 在同一 CPU 中配置线程应用程序和特定内核线程,如 network softirq 或驱动程序线程。
  • 对每个 CPU 上的 producer-consumer 线程配对。生产者和消费者是两类线程,生产者将数据插入到缓冲区中,消费者将其从缓冲区中删除。

在实时系统上调优关联性的常见做法是确定运行应用程序所需的内核数,然后隔离这些内核。您可以使用 Tuna 工具或使用 shell 脚本修改位掩码值(如 taskset 命令)来达到此目的。taskset 命令更改进程的关联性,并修改 /proc/ 文件系统条目会更改中断的关联性。

11.1. 使用 taskset 命令调整处理器关联性

在实时,taskset 命令有助于设置或检索正在运行的进程的 CPU 关联性。taskset 命令采用 -p-c 选项。The -p or-pid 选项可处理现有进程,且不会启动新的任务。-c or --cpu-list 指定处理器的数字列表,而不是 bitmask。列表中可以包含多个项目,用逗号分开,以及一系列处理器。例如: 0,5,7,9-11。

先决条件

  • 您在系统上具有 root 权限。

流程

  • 验证特定进程的进程关联性:

    # taskset -p -c 1000
    pid 1000’s current affinity list: 0,1
    Copy to Clipboard

    命令打印 PID 为 1000 的进程的关联性。进程设置为使用 CPU 0 或 CPU 1。

    • 可选:要将特定 CPU 配置为绑定进程:

      # taskset -p -c 1 1000
      pid 1000’s current affinity list: 0,1
      pid 1000’s new affinity list: 1
      Copy to Clipboard
    • 可选:定义多个 CPU 关联性:

      # taskset -p -c 0,1 1000
      pid 1000’s current affinity list: 1
      pid 1000’s new affinity list: 0,1
      Copy to Clipboard
    • 可选: 要在特定 CPU 上配置优先级级别和策略:

      # taskset -c 5 chrt -f 78 /bin/my-app
      Copy to Clipboard

      如需进一步的粒度,您还可以指定优先级和策略。在示例中,命令在带有 SCHED_FIFO 策略的 CPU 5 上运行 /bin/my-app 应用程序,其优先级值为 78。

11.2. 使用 sched_setaffinity ()系统调用设置处理器关联性

您还可以使用实时 sched_setaffinity () 系统调用来设置处理器关联性。

先决条件

  • 您在系统上具有 root 权限。

流程

  • 使用 sched_setaffinity () 设置处理器关联性:

    #define _GNU_SOURCE
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <sched.h>
    
    int main(int argc, char **argv)
    {
      int i, online=0;
      ulong ncores = sysconf(_SC_NPROCESSORS_CONF);
      cpu_set_t *setp = CPU_ALLOC(ncores);
      ulong setsz = CPU_ALLOC_SIZE(ncores);
    
      CPU_ZERO_S(setsz, setp);
    
      if (sched_getaffinity(0, setsz, setp) == -1) {
        perror("sched_getaffinity(2) failed");
        exit(errno);
      }
    
      for (i=0; i < CPU_COUNT_S(setsz, setp); i) {
        if (CPU_ISSET_S(i, setsz, setp))
          online;
      }
    
      printf("%d cores configured, %d cpus allowed in affinity mask\n", ncores, online);
      CPU_FREE(setp);
    }
    Copy to Clipboard

11.3. 隔离单个 CPU 以运行高利用率任务

使用 cpusets 机制,您可以为 SCHED_DEADLINE 任务分配一组 CPU 和内存节点。在使用任务具有高和低 CPU 的任务集合中,隔离 CPU 以在不同的 CPU 上运行高利用率任务和将小利用率任务调度到不同的 CPU 集合中,使所有任务能够满足所分配的 运行时。您必须手动为 'cpusets' 添加配置

先决条件

  • 您在系统上具有 root 权限。

流程

  1. 创建两个名为 cluster 和 partition 的控制组:

    # cd /sys/fs/cgroup
    # echo +cpuset > cgroup.subtree_control
    # mkdir cluster
    # mkdir partition
    # echo +cpuset | tee cluster/cgroup.subtree_control partition/cgroup.subtree_control
    Copy to Clipboard
  2. 在集群控制组群中,将低利用率任务调度到 CPU 1 到 7。验证内存大小,并将控制组命名为 exclusive :

    # cd cluster
    # echo 1-7 | tee cpuset.cpus cpuset.cpus.exclusive
    # echo root > cpuset.cpus.partition
    Copy to Clipboard
  3. 将所有低利用率任务移到集群控制组群中:

    # ps -eLo lwp | while read thread; do echo $thread > cgroup.procs ; done
    Copy to Clipboard
  4. 分区 控制组群中,分配高利用率任务:

    # echo 0 | tee cpuset.cpus cpuset.cpus.exclusive
    # echo isolated > cpuset.cpus.partition
    Copy to Clipboard
  5. 将 shell 添加到分区控制组中并启动:

    # echo $$ > cgroup.procs
    Copy to Clipboard

    在这个版本中,在分区 控制组群中隔离的任务不会影响 集群 控制组群中的任务。这可让所有实时任务满足调度程序期限。如果您使用截止时间调度程序,则在没有此更改的情况下通常会满足截止时间。请注意,其他任务都有自己的期限。

如果应用程序已准备好使用正确的固定,可以通过调整 cgroups 来进一步减少对 分区 cgroup 的 cpus,并为它分配所有实时任务:

# cd ..
# echo 4-7 | tee cluster/{cpuset.cpus,cpuset.cpus.exclusive}
# echo 0-3 | tee partition/{cpuset.cpus,cpuset.cpus.exclusive}
Copy to Clipboard

11.4. 减少 CPU 性能激增

常见延迟高峰来源是内核计时器循环处理器中常见锁定的多个 CPU 持续时。负责争用的常见锁定是 xtime_lock,由计时系统和 Read-Copy-Update (RCU)结构锁定使用。通过使用 skew_tick=1,您可以偏移每个 CPU 的计时器循环,以在不同时间启动,并避免潜在的锁定冲突。

skew_tick 内核命令行参数可能会阻止低到具有大型核心数的大型系统的延迟波动,并具有对延迟敏感的工作负载。

先决条件

  • 有管理员权限。

流程

  1. 使用 grubby 启用 skew_tick=1 参数。

    # grubby --update-kernel=ALL --args="skew_tick=1"
    Copy to Clipboard
  2. 重启以使更改生效。

    # reboot
    Copy to Clipboard
注意

启用 skew_tick=1 会导致功耗显著增加,因此只有在您运行对延迟敏感实时工作负载且一致性延迟时,必须启用 skew 引导参数。

验证

显示 /proc/cmdline 文件,并确保指定了 skew_tick=1/proc/cmdline 文件显示传递给内核的参数。

  • 检查 /proc/cmdline 文件中的新设置。

    # cat /proc/cmdline
    Copy to Clipboard

11.5. 禁用 PC 卡守护进程来降低 CPU 使用量

pcscd 守护进程管理到并行通信(PC 或 PCMCIA)和智能卡(SC)读取器的连接。虽然 pcscd 通常是一个低优先级的任务,但它通常使用比任何其他守护进程更多的 CPU。因此,额外的背景 noise 可能会导致更高的抢占成本进行实时任务,并会对确定性造成其他不良影响。

先决条件

  • 您在系统上具有 root 权限。

流程

  1. 检查 pcscd 守护进程的状态。

    # systemctl status pcscd
    ● pcscd.service - PC/SC Smart Card Daemon
         Loaded: loaded (/usr/lib/systemd/system/pcscd.service; indirect; vendor preset: disabled)
         Active: active (running) since Mon 2021-03-01 17:15:06 IST; 4s ago
    TriggeredBy: ● pcscd.socket
           Docs: man:pcscd(8)
       Main PID: 2504609 (pcscd)
          Tasks: 3 (limit: 18732)
         Memory: 1.1M
            CPU: 24ms
         CGroup: /system.slice/pcscd.service
                 └─2504609 /usr/sbin/pcscd --foreground --auto-exit
    Copy to Clipboard

    Active 参数显示 pcsd 守护进程的状态。

  2. 如果 pcsd 守护进程正在运行,请停止它。

    # systemctl stop pcscd
    Warning: Stopping pcscd.service, but it can still be activated by:
      pcscd.socket
    Copy to Clipboard
  3. 将系统配置为确保 pcsd 守护进程在系统启动时不会重启。

    # systemctl disable pcscd
    Removed /etc/systemd/system/sockets.target.wants/pcscd.socket.
    Copy to Clipboard

验证

  1. 检查 pcscd 守护进程的状态。

    # systemctl status pcscd
    ● pcscd.service - PC/SC Smart Card Daemon
         Loaded: loaded (/usr/lib/systemd/system/pcscd.service; indirect; vendor preset: disabled)
         Active: inactive (dead) since Mon 2021-03-01 17:10:56 IST; 1min 22s ago
    TriggeredBy: ● pcscd.socket
           Docs: man:pcscd(8)
       Main PID: 4494 (code=exited, status=0/SUCCESS)
            CPU: 37ms
    Copy to Clipboard
  2. 确保 Active 参数的值是 inactive (dead)

第 12 章 在 RHEL for Real Time 中使用 mlock ()系统调用

RHEL for Real-Time 内存锁定(mlock())函数可让实时调用进程锁定或解锁指定的地址空间范围。这个范围可防止 Linux 在交换内存空间时分页锁定的内存。将物理页面分配给页表条目后,对该页面的引用就会变得快速。mlock () 系统调用包含两个函数: mlock ()mlockall ()。同样,munlock () 系统调用包含 munlock ()munlockall () 函数。

12.1. 使用 mlock ()系统调用锁定页面

实时 mlock () 系统调用使用 addr 参数指定地址范围的开头,len 则以字节为单位定义地址空间长度。alloc_workbuf () 函数动态分配内存缓冲区并锁定它。内存分配由 posix_memalig () 函数完成,以将内存区域与页面保持一致。函数 free_workbuf () 解锁内存区域。

先决条件

  • 您有 root 特权或 CAP_IPC_LOCK 功能在大型缓冲区上使用 mlockall ()mlock ()

流程

  • 以下代码使用 mlock () 系统调用锁定页面:

    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/mman.h>
    
    void *alloc_workbuf(size_t size)
    {
      void *ptr;
      int retval;
    
      // alloc memory aligned to a page, to prevent two mlock() in the same page.
      retval = posix_memalign(&ptr, (size_t) sysconf(_SC_PAGESIZE), size);
    
      // return NULL on failure
      if (retval)
        return NULL;
    
      // lock this buffer into RAM
      if (mlock(ptr, size)) {
        free(ptr);
        return NULL;
      }
    
      return ptr;
    }
    
    void free_workbuf(void *ptr, size_t size) {
      // unlock the address range
      munlock(ptr, size);
    
      // free the memory
      free(ptr);
    }
    Copy to Clipboard

验证

成功时,实时 mlock ()munlock () 调用返回 0。如果出现错误,则返回 -1 并设置 errno 来指示错误。

12.2. 使用 mlockall ()系统调用锁定所有映射的页面

要使用 mlockall ()munlockall () 系统调用锁定和解锁实时内存,请将 flags 参数设置为 0 或一个常量: MCL_CURRENTMCL_FUTURE。使用 MCL_FUTURE 时,未来系统调用,如 mmap (2)sbrk (2)malloc (3) 可能会失败,因为它会导致锁定字节数超过允许的最大值。

先决条件

  • 您在系统上具有 root 权限。

流程

  • 使用 mlockall ()munlockall () 实时系统调用:

    • 使用 mlockall () 系统调用锁定所有映射的页面:

      #include <sys/mman.h>
      int mlockall (int flags)
      Copy to Clipboard
    • 使用 munlockall () 系统调用解锁所有映射的页面:

      #include <sys/mman.h>
      int munlockall (void)
      Copy to Clipboard

12.3. 使用 mmap ()系统调用将文件或设备映射到内存中

对于实时系统中的大型内存分配,内存分配(malloc)方法使用 mmap () 系统调用来查找内存空间。您可以通过在 flags 参数中设置 MAP_LOCKED 来分配和锁定内存区域。因为 mmap () 以页为基础分配内存,因此它会避免在同一页面中出现两个锁定,这样可防止双锁或单解锁问题。

先决条件

  • 您在系统上具有 root 权限。

流程

  • 映射特定的 process-address 空间:

    #include <sys/mman.h>
    #include <stdlib.h>
    
    void *alloc_workbuf(size_t size)
    {
     void *ptr;
    
     ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
                MAP_PRIVATE | MAP_ANONYMOUS | MAP_LOCKED, -1, 0);
    
     if (ptr == MAP_FAILED)
      return NULL;
    
     return ptr;
    }
    
    void
    free_workbuf(void *ptr, size_t size)
    {
     munmap(ptr, size);
    }
    Copy to Clipboard

验证

  • mmap () 函数成功完成时,它会将指针返回到映射区域。出错时,它会返回 MAP_FAILED 值,并设置 errno 来指示错误。
  • munmap () 函数成功完成时,它返回 0。出错时,它会返回 -1,并设置 errno 来指示错误。

12.4. mlock ()系统调用的参数

mlock 参数表中列出了内存锁定系统调用的参数及其执行功能。

表 12.1. mlock 参数
参数描述

addr

指定要锁定或解锁的进程地址空间。当 NULL 时,内核会选择内存中数据的页对齐安排。如果 addr 不是 NULL,内核会选择一个 nearby 页面边界,该边界始终高于 /proc/sys/vm/mmap_min_addr 文件中指定的值。

len

指定映射的长度,它必须大于 0。

fd

指定文件描述符。

prot

mmapmunmap 调用通过此参数定义所需的内存保护。prot 取 一个或组合 PROT_EXEC,PROT_READ,PROT_WRITEPROT_NONE 值。

标记

控制映射到映射相同文件的其他进程的映射可见性。它采用其中一个值: MAP_ANONYMOUSMAP_LOCKEDMAP_PRIVATEMAP_SHARED 值。

MCL_CURRENT

锁定当前映射到进程中的所有页面。

MCL_FUTURE

将模式设置为锁定后续内存分配。这些可能是增大堆和堆栈、新内存映射文件或共享内存区域所需的新页面。

第 13 章 使用 RHEL for Real Time 中的 timerlat 测量调度延迟

rtla-timerlat 工具是 timerlat 追踪器的接口。timerlat 追踪器查找实时线程的 wake-up 延迟源。timerlat 追踪器为每个 CPU 创建一个具有实时优先级的内核线程,这些线程将定期计时器设置为 wake,并返回到 sleep。在唤醒时,timerlat 会查找并收集信息,这对于调试操作系统计时器延迟非常有用。timerlat 追踪器生成输出,并在每次激活时打印以下两行:

  • timerlat 追踪程序定期打印计时器中断请求(IRQ)处理程序中看到的计时器延迟。这是在线程激活前在 hardirq 上下文中看到的第一个输出。
  • 第二个输出是线程的计时器延迟。ACTIVATION ID 字段显示中断请求(IRQ)性能,以及它们对应的线程执行。

13.1. 配置 timerlat 追踪器来测量调度延迟

您可以通过在追踪系统的 curret_tracer 文件中添加 timerlat 来配置 timerlat 追踪器。current_tracer 文件通常挂载到 /sys/kernel/tracing 目录中。timerlat 追踪器测量中断请求(IRQ),并在线程延迟超过 100 微秒时保存用于分析的 trace 输出。

流程

  1. 列出当前的 tracer:

    # cat /sys/kernel/tracing/current_tracer
    nop
    Copy to Clipboard

    没有操作 (nop)是默认的 tracer。

  2. 在追踪系统的 current_tracer 文件中添加 timerlat 追踪器:

    # cd /sys/kernel/tracing/
    # echo timerlat > current_tracer
    Copy to Clipboard
  3. 生成追踪输出:

    # cat trace
    # tracer: timerlat
    Copy to Clipboard

验证

  • 输入以下命令检查 timerlat 是否作为当前的 tracer 启用:

    # cat /sys/kernel/tracing/current_tracer
    timerlat
    Copy to Clipboard

13.2. timerlat 追踪器选项

timerlat 追踪器基于 osnoise tracer 构建。因此,您可以将 /osnoise/config 目录中的选项设置为 trace 并捕获线程调度延迟的信息。

timerlat 选项

cpus
为要执行的 计时器线程 设置 CPU。
timerlat_period_us
以微秒为单位设置 timerlat 线程的持续时间。
stop_tracing_us
如果 irq 上下文的计时器延迟超过配置的值,则停止系统追踪。编写 0 可禁用这个选项。
stop_tracing_total_us
如果总 noise 大于配置的值,则停止系统追踪。编写 0 可禁用这个选项。
print_stack
保存出现的中断请求的堆栈(IRQ)。堆栈会在线程上下文事件后保存 IRQ 发生,或者 IRQs 处理程序超过配置的值。

13.3. 使用 rtla-timerlat-top 测量计时器延迟

rtla-timerlat-top tracer 显示 timerlat tracer 中定期输出的摘要。tracer 输出还提供有关每个操作系统声明和事件的信息,如 osnoise追踪点。您可以使用 the -t 选项查看此信息。

流程

  • 测量计时器延迟:

    # rtla timerlat top -s 30 -T 30 -t
    Copy to Clipboard

13.4. rtla timerlat 顶部 tracer 选项

通过使用 rtla timerlat top --help 命令,您可以查看 rtla-timerlat-top tracer 选项的帮助用法。

timerlat-top-tracer 选项

-p,--period us
以微秒为单位设置 timerlat 追踪器周期。
-i, --irq us
如果中断请求(IRQ)延迟超过微秒中的参数,则停止追踪。
-t, --thread us
如果线程延迟超过微秒中的参数,则停止追踪。
-t,--trace
将已停止的 trace 保存到 timerlat_trace.txt 文件。
-s, --stack us
如果线程延迟超过参数,请在中断请求(IRQ)中保存堆栈跟踪。

第 14 章 使用 RHEL for Real Time 中的 rtla-osnoise 测量调度延迟

低延迟是一个环境,它经过优化来处理具有低容错(延迟)的大量数据。为应用程序提供专用资源(包括 CPU)是在大量低延迟环境中进行的。例如,对于网络功能虚拟化(NFV)应用中的高性能网络处理,单个应用具有 CPU 电源限制集来持续运行任务。

Linux 内核包含实时分析(rtla)工具,它为操作系统 noise (osnoise) tracer 提供了一个接口。操作系统声明是应用中因操作系统内活动而进行的干扰。Linux 系统可能会因为以下原因而体验:

  • 不可屏蔽中断(NMI)
  • 中断请求(IRQ)
  • 软中断请求(SoftIRQ)
  • 其他系统线程活动
  • 与硬件相关的作业,如不可屏蔽的高优先级系统管理中断(SMI)

14.1. rtla-osnoise tracer

Linux 内核包含实时分析(rtla)工具,它为操作系统 noise (osnoise) tracer 提供了一个接口。rtla-osnoise tracer 创建一个在指定时间段内定期运行的线程。在 句点 开始时,线程会禁用中断,启动抽样,并在循环中捕获时间。

rtla-osnoise tracer 提供以下功能:

  • 测量 CPU 接收的操作量。
  • 特征在 CPU 中发生的操作系统声明类型。
  • 输出优化的 trace 报告,以帮助定义意外结果的根本原因。
  • 节省每个干扰源的干扰计数器。非可屏蔽中断(NMI)、中断请求(IRQ)、软件中断请求(SoftIRQ)和线程的干扰计数器,当工具检测到这些干扰的条目事件时,线程会增加。

rtla-osnoise tracer 会打印一个运行报告,并在周期的结论中提供有关 noise 源的以下信息:

  • 无权总额.
  • 最多声明量。
  • 分配给线程的 CPU 百分比。
  • noise 源的计数器。

14.2. 配置 rtla-osnoise tracer 以测量调度延迟

您可以通过在追踪系统的 curret_tracer 文件中添加 osnoise 来配置 rtla-osnoise tracer。current_tracer 文件通常挂载到 /sys/kernel/tracing/ 目录中。rtla-osnoise 追踪器测量中断请求(IRQ),并在线程延迟超过 20 微秒内保存分析的 trace 输出。

流程

  1. 列出当前的 tracer:

    # cat /sys/kernel/tracing/current_tracer
    nop
    Copy to Clipboard

    没有操作 (nop)是默认的 tracer。

  2. 在追踪系统的 current_tracer 文件中添加 timerlat 追踪器:

    # cd /sys/kernel/tracing/
    # echo osnoise > current_tracer
    Copy to Clipboard
  3. 生成追踪输出:

    # cat trace
    # tracer: osnoise
    Copy to Clipboard

14.3. 配置的 rtla-osnoise 选项

rtla-osnoise tracer 的配置选项位于 /sys/kernel/tracing/ 目录中。

rtla-osnoise的配置选项

osnoise/cpus
配置要运行的 osnoise 线程的 CPU。
osnoise/period_us
配置 osnoise 线程的 期间
osnoise/runtime_us
配置 osnoise 线程的运行持续时间。
osnoise/stop_tracing_us
如果单个 noise 大于配置的值,则停止系统追踪。设置 0 可禁用这个选项。
osnoise/stop_tracing_total_us
如果总 noise 大于配置的值,则停止系统追踪。设置 0 可禁用这个选项。
tracing_thresh
设置两个 time () 调用读取之间的最小 delta,以微秒为单位被视为 noise。当设置为 0 时,tracing_thresh 将使用默认值,即 5 微秒。

14.4. rtla-osnoise 追踪点

rtla-osnoise 包括一组用来识别操作系统发行源的 追踪点 (osnoise)。

rtla-osnoise的 trace 点

osnoise:sample_threshold
当 noise 超过配置的阈值(tolerance_ns)时,显示 noise。
osnoise:nmi_noise
显示不可屏蔽中断(NMI)的 noise 和 noise 持续时间。
osnoise:irq_noise
显示中断请求(IRQ)的 noise 和 noise 持续时间。
osnoise:softirq_noise
显示来自软中断请求的 noise 和 noise 持续时间(SoftIRQ)。
osnoise:thread_noise
显示线程中的 noise 和 noise 持续时间。

14.5. rtla-osnoise tracer 选项

osnoise/options 文件包括一组用于 rtla-osnoise tracer 的配置选项。

rtla-osnoise的选项

默认值
将选项重置为默认值。
OSNOISE_WORKLOAD
停止 osnoise 工作负载分配。
PANIC_ON_STOP
如果 tracer 停止,则设置 panic () 调用。这个选项捕获 vmcore 转储文件。
OSNOISE_PREEMPT_DISABLE
禁用对 osnoise 工作负载的抢占,仅允许中断请求(IRQ)和与硬件相关的通知。
OSNOISE_IRQ_DISABLE
osnoise 工作负载禁用中断请求(IRQ),这只允许不可屏蔽中断(NMI)和硬件相关的 noise。

14.6. 使用 rtla-osnoise-top tracer 测量操作系统noise

rtla osnoise-top tracer 测量并显示 osnoise tracer 的定期摘要,以及有关 interference 源发生计数器的信息。

流程

  1. 测量系统 noise:

    # rtla osnoise top -P F:1 -c 0-3 -r 900000 -d 1M -q
    Copy to Clipboard

    命令输出显示定期摘要,其中包含有关实时优先级的信息、要运行线程的 CPU 以及以微秒为单位运行的周期。

14.7. rtla-osnoise-top tracer 选项

通过使用 rtla osnoise top --help 命令,您可以查看 rtla-osnoise-top tracer 可用选项的帮助用法。

rtla-osnoise-top的选项

-a,--auto us
设置自动追踪模式。此模式在调试系统时设置一些常用的选项。它相当于使用 -s us -T 1-t
-p,--period us
以微秒为单位设置 osnoise tracer 持续时间。
-r, --runtime us
以微秒为单位设置 osnoise tracer 运行时。
-s, --stop us
如果单个示例超过微秒中的参数,则停止追踪。使用 ,命令可将 trace 保存到输出中。
-s, --stop-total us
如果示例总数超过微秒中的参数,则停止追踪。使用 -T 时,命令会将 trace 保存到输出中。
-t, --threshold us
指定两个时间读取之间的最小 delta 被视为 noise。默认阈值是 5 us。
-q, --quiet
仅在运行结束时打印摘要。
-c, --cpus cpu-list
设置 osnoise tracer,以在分配的 cpu-list 上运行示例线程。
-d, --duration time[s|m|h|d]
设置运行的持续时间。
-D, --debug
打印调试信息。
-t, --trace[=file]
将已停止的 trace 保存到 [file|osnoise_trace.txt] 文件。
-e, --event sys:event
在 trace (-t)会话中启用事件。参数可以是特定的事件,如 sched:sched_switch,或者系统组的所有事件,如 sched 系统组。
--filter &lt ;filter>
使用过滤器表达式过滤 previous -e sys:event 系统事件。
--trigger <trigger>
启用对 previous -e sys:event 系统事件的 trace 事件触发器。
-p, --priority o:prio|r:prio|f:prio|d:runtime:period
将调度参数设置为 osnoise tracer 线程。
-h, --help
打印帮助菜单。

第 15 章 尽量减少或避免系统因为日志而减慢的问题

将日志更改写入到磁盘的顺序可能与它们到达的顺序不同。内核 I/O 系统可以重新排序日志更改,以优化可用存储空间的使用。日志活动可以通过重新排序日志更改并提交数据和元数据导致系统延迟。因此,日志记录文件系统可能会减慢系统的速度。

XFS 是 RHEL 8 使用的默认文件系统。这是一个日志文件系统。名为 ext2 的旧文件系统不使用日志。除非您的机构特别需要日志记录,否则请考虑 ext2 文件系统。在许多红帽最佳基准结果中,会使用 ext2 文件系统。这是顶级初始调优建议之一。

XFS 等日志记录文件系统记录上次访问文件的时间( atime 属性)。如果您需要使用日志记录文件系统,请考虑禁用 atime

15.1. 禁用 atime

禁用 atime 属性通过限制对文件系统日志的写入数量来提高性能并降低功耗。

流程

  1. 使用您选择的文本编辑器打开 /etc/fstab 文件,并找到 root 挂载点的条目。

    /dev/mapper/rhel-root       /       xfs    defaults…
    Copy to Clipboard
  2. 编辑 options 部分,使其包含术语 noatimenodiratimenoatime 选项可防止在文件读取时更新访问时间戳,nodiratime 选项会停止更新目录内节点访问时间。

    /dev/mapper/rhel-root       /       xfs    noatime,nodiratime…
    Copy to Clipboard
重要

有些应用程序 依赖于 一次更新。因此,这个选项只适用于没有使用此类应用程序的系统。

或者,您可以使用 relatime 挂载选项,该选项可确保仅在以前的访问时间超过当前修改时间时才更新访问时间。

第 16 章 禁用对延迟敏感工作负载的图形控制台输出

内核会在系统启动时马上将消息传递给 printk ()。内核将消息发送到日志文件,也显示在图形控制台中,即使没有附加到无头服务器的监控器。

在一些系统中,发送到图形控制台的输出可能会在管道中引入停滞。这可能会在等待数据传输时造成任务执行的潜在延迟。例如,发送到 teletype0 (/dev/tty0) 的输出可能会在某些系统中造成潜在的停止状态。

要防止意外的停滞,您可以通过以下方法限制或禁用发送到图形控制台的信息:

  • 删除 tty0 定义。
  • 更改控制台定义的顺序.
  • 关闭大多数 printk () 函数,并确保将 ignore_loglevel 内核参数设置为 未配置

通过禁用图形控制台输出的日志记录和控制图形控制台上打印的消息,您可以提高敏感工作负载的延迟。

16.1. 禁用图形控制台日志记录到图形适配器

teletype (tty)默认内核控制台通过将输入数据传递给系统并显示有关图形控制台的输出信息来启用与系统交互。

没有配置图形控制台,防止它在图形适配器上记录。这使得系统无法使用 tty0,帮助禁用图形控制台上的打印消息。

注意

禁用图形控制台输出不会删除信息。这些信息会在系统日志中打印,您可以使用 journalctldmesg 实用程序访问它们。

流程

  • 从内核配置中删除 console=tty0 选项:

    # grubby --update-kernel=ALL --remove-args="console=tty0"
    Copy to Clipboard

16.2. 禁用在图形控制台中打印的消息

您可以通过在 /proc/sys/kernel/printk 文件中配置所需的日志级别来控制发送到图形控制台的输出消息量。

流程

  1. 查看当前的控制台日志级别:

    $ cat /proc/sys/kernel/printk
      7    4    1    7
    Copy to Clipboard

    命令打印系统日志级别的当前设置。数字对应于系统日志记录器的当前、默认、最小和最大值。

  2. /proc/sys/kernel/printk 文件中配置所需的日志级别。

    $ echo "1” > /proc/sys/kernel/printk
    Copy to Clipboard

    命令更改当前的控制台日志级别。例如,设置日志级别 1 将仅打印警报消息,并防止在图形控制台中显示其他消息。

第 17 章 管理系统时钟以满足应用程序的需求

NUMA 或 SMP 等多处理器系统有多个硬件时钟实例。在启动过程中,内核会发现可用的时钟源并选择一个要使用的源。要提高性能,您可以更改用于满足实时系统的最低要求的时钟源。

17.1. 硬件时钟

在多处理器系统中发现多个时钟源的实例,如非统一内存访问(NUMA)和 Symmetric 多处理(SMP),以及它们对系统事件做出反应,如 CPU 频率扩展或进入能源 economy 模式,确定它们是否适合实时内核时钟源。

首选时钟源是时间戳计数器(TSC)。如果 TSC 不可用,则高精度事件时间(HPET)是第二个最佳选项。但是,并非所有系统都有 HPET 时钟,一些 HPET 时钟可能并不可靠。

如果没有 TSC 和 HPET,其他选项包括 ACPI Power Management Timer (ACPI_PM)、Programmable Interval Timer (PIT)和 Real Time Clock (RTC)。最后两个选项可昂贵地读取或具有低分辨率(时间粒度),因此它们适合用于实时内核。

17.2. 查看当前正在使用的时钟源

系统中当前使用的时钟源保存在 /sys/devices/system/clocksource/clocksource0/current_clocksource 文件中。

流程

  • 显示 current_clocksource 文件。

    # cat /sys/devices/system/clocksource/clocksource0/current_clocksource
    tsc
    Copy to Clipboard

    在本例中,系统中的当前时钟源是 TSC。

17.3. 临时更改要使用的时钟源

有时,由于时钟中已知的问题,系统主应用程序的最佳时钟不会被使用。在完成所有有问题的时钟后,系统可以保留无法满足实时系统的最低要求的硬件时钟。

关键应用程序的要求因每个系统而异。因此,每个应用程序的最佳时钟,因此每个系统都有所不同。有些应用程序依赖于时钟解析,提供可靠的纳秒读取的时钟可能更合适。读取时钟经常读取时钟的应用程序经常会受益于具有较小的读取成本的时钟(读取请求和结果之间的时间)。

在这些情况下,可以覆盖内核选择的时钟,前提是您了解覆盖的副作用,并可创建环境来触发给定硬件时钟的已知不足。

重要

内核会自动选择最适合的时钟源。不建议覆盖所选时钟源,除非有很好的理解。

先决条件

  • 您在系统上具有 root 权限。

流程

  1. 查看可用的时钟源。

    # cat /sys/devices/system/clocksource/clocksource0/available_clocksource
    tsc hpet acpi_pm
    Copy to Clipboard

    例如,考虑系统中可用的时钟源是 TSC、HPET 和 ACPI_PM。

  2. 将您要使用的时钟源名称写入 /sys/devices/system/clocksource/clocksource0/current_clocksource 文件。

    # echo hpet > /sys/devices/system/clocksource/clocksource0/current_clocksource
    Copy to Clipboard

验证

  • 显示 current_clocksource 文件,以确保当前时钟源是指定的时钟源。

    # cat /sys/devices/system/clocksource/clocksource0/current_clocksource
    hpet
    Copy to Clipboard

    这个示例使用 HPET 作为系统中当前的时钟源。

17.4. 读取硬件时钟源的成本比较

您可以比较系统中的时钟速度。从 TSC 读取涉及从处理器读取寄存器。从 HPET 时钟读取涉及读取内存区域。从 TSC 读取速度快,当每秒时间戳上数以千计的信息时,这提供了显著的性能优势。

先决条件

  • 您在系统上具有 root 权限。
  • clock_timing 程序必须在系统上。如需更多信息,请参阅 clock_timing 程序

流程

  1. 进入保存 clock_timing 程序的目录。

    # cd clock_test
    Copy to Clipboard
  2. 查看系统中的可用时钟源。

    # cat /sys/devices/system/clocksource/clocksource0/available_clocksource
    tsc hpet acpi_pm
    Copy to Clipboard

    在本例中,系统中的可用时钟源为 TSCHPETACPI_PM

  3. 查看当前使用的时钟源。

    # cat /sys/devices/system/clocksource/clocksource0/current_clocksource
    tsc
    Copy to Clipboard

    在本例中,系统中的当前时钟源是 TSC

  4. 运行 time 工具以及 ./clock_timing 程序。输出显示了读取时钟源 1,000万次所需的持续时间。

    # time ./clock_timing
    
    	real	0m0.601s
    	user	0m0.592s
    	sys	0m0.002s
    Copy to Clipboard

    示例显示了以下参数:

    • 实时 - 从程序调用开始的总时间,直到进程结束。real 包括用户和内核时间,并且通常大于后者的总和。如果此进程被优先级更高的应用中断,或者被系统事件(如硬件中断(IRQ))中断,那么此等待时间也被 实时计算
    • 用户 - 执行用户空间中进程执行不需要内核干预的任务所花费的时间。
    • sys - 在执行用户进程所需任务的同时,内核花费的时间。这些任务包括打开文件、读取和写入文件或 I/O 端口、内存分配、线程创建和网络相关活动。
  5. 将您要测试的下一个时钟源的名称写入 /sys/devices/system/clocksource/clocksource0/current_clocksource 文件。

    # echo hpet > /sys/devices/system/clocksource/clocksource0/current_clocksource
    Copy to Clipboard

    在本例中,当前的时钟源被改为 HPET

  6. 对所有可用时钟源重复步骤 4 和 5。
  7. 比较所有可用时钟源的第 4 步结果。

17.5. 在 Opteron CPU 上同步 TSC 计时器

当前的 AMD64 Opteron 处理器对于一个大型 gettimeofday skew 可能很易受。当 cpufreq时间戳计数器 (TSC)都被使用时,会出现这个偏移。RHEL for Real Time 提供了通过强制所有处理器同时更改为相同频率来防止这种偏移的方法。因此,单个处理器上的 TSC 不会以不同于其他处理器上的 TSC 的速度增加。

先决条件

  • 您在系统上具有 root 权限。

流程

  1. 启用 clocksource=tscpowernow-k8.tscsync=1 内核选项:

    # grubby --update-kernel=ALL --args="clocksource=tsc powernow-k8.tscsync=1"
    Copy to Clipboard

    这会强制使用 TSC,并启用同时核心处理器频率转换。

  2. 重启机器。

17.6. clock_timing 程序

clock_timing 程序读取当前的时钟源 1,000万次。与 time 实用程序结合使用时,它会测量执行此操作所需的时间。

流程

创建 clock_timing 程序:

  1. 为程序文件创建一个目录。

    $ mkdir clock_test
    Copy to Clipboard
  2. 更改到创建的目录。

    $ cd clock_test
    Copy to Clipboard
  3. 创建源文件,并在文本编辑器中打开。

    $ {EDITOR} clock_timing.c
    Copy to Clipboard
  4. 在文件中输入以下内容:

    #include <time.h>
    void main()
    {
      int rc;
      long i;
      struct timespec ts;
    
      for(i=0; i<10000000; i++) {
        rc = clock_gettime(CLOCK_MONOTONIC, &ts);
      }
    }
    Copy to Clipboard
  5. 保存文件并退出编辑器。
  6. 编译文件。

    $ gcc clock_timing.c -o clock_timing -lrt
    Copy to Clipboard

    clock_timing 程序已就绪,可以从保存它的目录运行。

第 18 章 控制电源管理转换

您可以控制电源管理转换来提高延迟。

先决条件

  • 您在系统上具有 root 权限。

18.1. 节能状态

现代处理器主动从较低状态过渡到更高的节能状态(C-states)。不幸的是,从高节能状态转换到运行状态可能会消耗比实时应用程序的最佳时间。为防止这些转换,应用可以使用电源管理服务质量(PM QoS)接口。

通过 PM QoS 接口,系统可以模拟 idle=pollprocessor.max_cstate=1 参数的行为,但更精细地控制节能状态。idle=poll 可防止处理器进入 空闲状态processor.max_cstate=1 可防止处理器进入更深的 C-states (energy-saving 模式)。

当应用程序打开 /dev/cpu_dma_latency 文件时,pm QoS 接口会阻止处理器进入深度睡眠状态,这会在退出时造成意外延迟。当文件关闭时,系统会返回节能状态。

18.2. 配置电源管理状态

您可以通过使用以下方法配置电源管理状态来控制电源管理转换:

  • 将值写入 /dev/cpu_dma_latency 文件,以微秒为单位更改进程的最大响应时间,并保持文件描述符,直到需要低延迟为止。
  • 引用应用程序或脚本中的 /dev/cpu_dma_latency 文件。

先决条件

  • 有管理员特权。

流程

  • 通过编写 32 位数字来指定延迟容错,它代表 /dev/cpu_dma_latency 中的最大响应时间,并使文件描述符保持通过低延迟操作打开。0 代表完全禁用 C-state。

    例如:

    import os
    import signal
    import sys
    if not os.path.exists('/dev/cpu_dma_latency'):
        print("no PM QOS interface on this system!")
        sys.exit(1)
    try:
        fd = os.open('/dev/cpu_dma_latency', os.O_WRONLY)
        os.write(fd, b'\0\0\0\0')
        print("Press ^C to close /dev/cpu_dma_latency and exit")
        signal.pause()
    except KeyboardInterrupt:
        print("closing /dev/cpu_dma_latency")
        os.close(fd)
        sys.exit(0)
    Copy to Clipboard
    注意

    Power Management Quality of Service interface (pm_qos)接口仅在具有打开文件描述符时才活跃。因此,您用于访问 /dev/cpu_dma_latency 的任何脚本或程序都必须保存文件打开,直到允许 power-state 转换。

第 19 章 通过隔离中断和用户进程来最小化系统延迟

在响应各种事件时,实时环境需要最小化或消除延迟。要做到这一点,您可以将中断(IRQ)与不同专用 CPU 上的用户进程隔离开来。

19.1. 中断和进程绑定

将中断(IRQ)与不同专用 CPU 上的用户进程隔离,可在实时环境中最小化或消除延迟。

中断通常在 CPU 之间平均共享。当 CPU 必须编写新数据和指令缓存时,这可能会延迟中断处理。这些中断延迟可能会导致与在同一 CPU 中执行的其他处理冲突。

可以将时间关键中断和进程分配到特定的 CPU (或一系列 CPU)。这样,用于处理此中断的代码和数据结构很可能在处理器和指令缓存中。因此,专用进程可以尽快运行,所有其他非关键进程都在其他 CPU 上运行。这特别重要,其中涉及的速度接近或内存限制以及可用的外围总线带宽。任何等待内存进入处理器缓存都会对整个处理时间和确定性造成显著影响。

实际上,最佳性能完全特定于应用程序。例如,为不同公司调优具有相似功能的应用程序,需要完全不同的性能调整。

  • 当公司隔离 4 个 CPU 用于操作系统功能和中断处理时,公司看到了最佳结果。剩余的 2 个 CPU 专门用于应用程序处理。
  • 当公司将网络相关应用程序进程绑定到处理网络设备驱动程序中断的单一 CPU 上时,公司发现了最佳确定性。
重要

要将进程绑定到 CPU,通常需要知道给定 CPU 或 CPU 范围的 CPU 掩码。根据您使用的命令,CPU 掩码通常以 32 位位掩码、十进制数或十六进制数字表示。

表 19.1. 给定 CPU 的 CPU 掩码示例

CPU

bitmask

十进制

十六进制

0

00000000000000000000000000000001

1

0x00000001

0, 1

00000000000000000000000000000011

3

0x00000011

19.2. 禁用 irqbalance 守护进程

irqbalance 守护进程默认是启用的,并定期强制由 CPU 处理中断。但是,在实时部署中,不需要 irqbalance,因为应用程序通常绑定到特定的 CPU。

流程

  1. 检查 irqbalance 的状态。

    # systemctl status irqbalance
    irqbalance.service - irqbalance daemon
       Loaded: loaded (/usr/lib/systemd/system/irqbalance.service; enabled)
       Active: active (running) …
    Copy to Clipboard
  2. 如果 irqbalance 正在运行,请禁用它并停止它。

    # systemctl disable irqbalance
    # systemctl stop irqbalance
    Copy to Clipboard

验证

  • 检查 irqbalance 的状态是否为不活动。

    # systemctl status irqbalance
    Copy to Clipboard

19.3. 从 IRQ 平衡中排除 CPU

您可以使用 IRQ 平衡服务指定您要排除哪些 CPU 进行中断(IRQ)平衡。/etc/sysconfig/irqbalance 配置文件中的 IRQBALANCE_BANNED_CPUS 参数控制这些设置。参数的值是一个 64 位十六进制位掩码,掩码的每个位代表 CPU 内核。

流程

  1. 在首选文本编辑器中打开 /etc/sysconfig/irqbalance,找到名为 IRQBALANCE_BANNED_CPUS 的文件的部分。

    # IRQBALANCE_BANNED_CPUS
    # 64 bit bitmask which allows you to indicate which cpu's should
    # be skipped when reblancing irqs. Cpu numbers which have their
    # corresponding bits set to one in this mask will not have any
    # irq's assigned to them on rebalance
    #
    #IRQBALANCE_BANNED_CPUS=
    Copy to Clipboard
  2. 取消注释 IRQBALANCE_BANNED_CPUS 变量。
  3. 输入适当的位掩码,以指定 IRQ 平衡机制所忽略的 CPU。
  4. 保存并关闭该文件。
  5. 重启 irqbalance 服务以使更改生效:

    # systemctl restart irqbalance
    Copy to Clipboard
注意

如果您正在运行具有最多 64 个 CPU 内核的系统,请使用逗号分隔每组八个十六进制数字。例如: IRQBALANCE_BANNED_CPUS=00000001,0000ff00

表 19.2. 例子

CPU

bitmask

0

00000001

8 - 15

0000ff00

8 - 15, 33

00000002,0000ff00

注意

在 RHEL 7.2 及更高版本中,如果 /etc/sysconfig/ irqbalance 中没有在 /etc/sysconfig/irqbalance 中设置 IRQBALANCE_BANNED_CPUS,则 irqbalance 工具会自动避免通过 isolcpus 内核参数隔离的 CPU 内核中的 IRQ。

19.4. 手动将 CPU 关联性分配给单个 IRQ

分配 CPU 关联性可让将进程和未绑定进程和线程绑定到指定的 CPU 或 CPU 范围。这可减少缓存问题。

流程

  1. 通过查看 /proc/interrupts 文件,检查每个设备使用的 IRQ。

    # cat /proc/interrupts
    Copy to Clipboard

    每行都显示 IRQ 编号,每个 CPU 中发生中断数,后跟 IRQ 类型和描述。

             CPU0       CPU1
    0:   26575949         11         IO-APIC-edge  timer
    1:         14          7         IO-APIC-edge  i8042
    Copy to Clipboard
  2. 将 CPU 掩码写入特定 IRQ 的 smp_affinity 条目。CPU 掩码必须以十六进制数字表示。

    例如,以下命令指示 IRQ 编号 142 仅在 CPU 0 上运行。

    # echo 1 > /proc/irq/142/smp_affinity
    Copy to Clipboard

    更改仅在中断发生时生效。

验证

  1. 执行触发指定中断的活动。
  2. 检查 /proc/interrupts 的更改。

    配置的 IRQ 的指定 CPU 上的中断数量会增加,且在指定关联性外的 CPU 上配置的 IRQ 的中断数量不会增加。

19.5. 使用 taskset 工具将进程绑定到 CPU

taskset 实用程序使用任务的进程 ID (PID)来查看或设置其 CPU 关联性。您可以使用 实用程序运行带有所选 CPU 关联性的命令。

要设置关联性,您需要将 CPU 掩码设为十进制或十六进制数字。mask 参数是一个 位掩码,用于指定修改的命令或 PID 的 CPU 内核是法律的。

重要

taskset 工具在 NUMA (Non-Uniform Memory Access)系统上工作,但它不允许用户将线程绑定到 CPU 和最接近的 NUMA 内存节点。在这样的系统上,taskset 不是首选工具,而 numactl 实用程序则应用于其高级功能。

如需更多信息,请参阅您系统上的 numactl (8) 手册页。

流程

  • 使用必要的选项和参数运行 taskset

    • 您可以使用 -c 参数而不是 CPU 掩码来指定 CPU 列表。在本例中,会指示 my_embedded_process 仅在 CPU 0,4,7-11 上运行。

      # taskset -c 0,4,7-11 /usr/local/bin/my_embedded_process
      Copy to Clipboard

      在大多数情况下,此调用更为方便。

    • 要设置当前没有运行的进程的关联性,请使用 taskset 并指定 CPU 掩码和进程。

      在本例中,会指示 my_embedded_process 仅使用 CPU 3 (使用 CPU 掩码的十进制版本)。

      # taskset 8 /usr/local/bin/my_embedded_process
      Copy to Clipboard
    • 您可以在位掩码中指定多个 CPU。在本例中,将指示 my_embedded_process 在处理器 4、5、6 和 7 (使用 CPU 掩码的十六进制版本)上执行。

      # taskset 0xF0 /usr/local/bin/my_embedded_process
      Copy to Clipboard
    • 您可以使用 CPU 掩码和您要更改的进程的 PID 选项为已经运行的进程设置 CPU 关联性。 在本例中,PID 为 7013 的进程被指示仅在 CPU 0 上运行。

      # taskset -p 1 7013
      Copy to Clipboard
注意

您可以组合列出的选项。

第 20 章 管理内存不足状态

内存不足(OOM)是分配所有可用内存(包括交换空间)的计算状态。通常,这会导致系统 panic 并按预期停止工作。提供的说明有助于避免系统中的 OOM 状态。

先决条件

  • 您在系统上具有 root 权限。

20.1. 更改内存不足值

/proc/sys/vm/panic_on_oom 文件包含一个控制内存不足(OOM)行为的交换机的值。如果文件包含 1,则 OOM 上的内核 panics 并按预期停止运行。

默认值为 0, 它指示内核在系统处于 OOM 状态时调用 oom_killer () 函数。通常,oom_killer () 会终止不必要的进程,从而使系统可以存活。

您可以更改 /proc/sys/vm/panic_on_oom 的值。

流程

  1. 显示 /proc/sys/vm/panic_on_oom 的当前值。

    # cat /proc/sys/vm/panic_on_oom
    0
    Copy to Clipboard

    要更改 /proc/sys/vm/panic_on_oom 中的值:

  2. /proc/sys/vm/panic_on_oom 回显新值。

    # echo 1 > /proc/sys/vm/panic_on_oom
    Copy to Clipboard
注意

建议您在 OOM 上进行 Real-Time 内核panic。否则,当系统遇到 OOM 状态时,它不再确定。

验证

  1. 显示 /proc/sys/vm/panic_on_oom 的值。

    # cat /proc/sys/vm/panic_on_oom
    1
    Copy to Clipboard
  2. 验证显示的值是否与指定的值匹配。

20.2. 在内存不足状态时,要终止的进程的优先级

您可以优先选择 oom_killer () 函数终止的进程。这样可保证高优先级进程在 OOM 状态期间保持运行。每个进程都有一个目录 /proc/PID。每个目录包括以下文件:

  • oom_score_adj ' - 'oom_score_adj 的有效分数,范围为 -16 到 +15。这个值用于计算进程的性能空间,该算法也考虑了运行进程的时间以及其它因素。
  • oom_score - 包含使用 oom_score_adj 中的值计算的算法结果。

在内存不足状态下,oom_killer () 函数终止具有最高 oom_score 的进程。

您可以通过编辑进程的 'oom_score_adj ' 文件来对进程终止的优先级。

先决条件

  • 知道您要优先级的进程 ID (PID)。

流程

  1. 显示进程的当前 oom_score

    # cat /proc/12465/oom_score
    79872
    Copy to Clipboard
  2. 为进程显示 oom_score_adj 的内容。

    # *cat /proc/12465/oom_score_adj *
    13
    Copy to Clipboard
  3. 编辑 oom_score_adj 中的值。

    # *echo -5 > /proc/12465/oom_score_adj *
    Copy to Clipboard

验证

  1. 显示进程的当前 oom_score

    # cat /proc/12465/oom_score
    78
    Copy to Clipboard
  2. 验证显示的值是否低于上一个值。

20.3. 为进程禁用内存不足终止程序

您可以通过将 oom_score_adj 设置为 -17 的保留值来禁用进程的 oom_killer () 函数。这将使进程保持活动状态,甚至处于 OOM 状态。

流程

  • oom_score_adj 中的值设置为 -17

    # echo -17 > /proc/12465/oom_score_adj
    Copy to Clipboard

验证

  1. 显示进程的当前 oom_score

    # cat /proc/12465/oom_score
    0
    Copy to Clipboard
  2. 验证显示的值是否为 0。

第 21 章 使用 tuna CLI 改进延迟

您可以使用 tuna CLI 来提高系统上的延迟。RHEL 10 的 tuna CLI 包括命令行,该命令行基于 argparse 解析模块。接口提供以下功能:

  • 更标准化的命令和选项菜单
  • 使用接口,您可以使用预定义的输入和 tuna 可确保输入正确类型
  • 在如何使用参数时自动生成使用帮助消息,并提供带有无效参数的错误消息

21.1. 先决条件

  • tunapython-linux-procfs 软件包已安装。
  • 您在系统上具有 root 权限。

21.2. tuna CLI

tuna 命令行界面(CLI)是一个帮助您对系统进行调优更改的工具。

tuna 工具设计为在运行的系统中使用,并立即进行更改。这允许任何特定应用程序的测量工具在更改后马上查看和分析系统性能。

tuna CLI 现在有一组命令,它们之前是操作选项。这些命令是:

隔离
将所有线程和 IRQ 移出 CPU-LIST
Include
将所有线程配置为在 CPU LIST 上运行。
Move
将特定的实体移到 CPU-LIST
spread
将所选实体分散到 CPU LIST 上。
priority
设置线程调度程序可调项,如 POLICYRTPRIO
run
分叉新进程并运行 命令。
save
kthreads sched 可调项 保存到 FILENAME
apply
应用配置文件中定义的更改。
show_threads
显示线程列表。
show_irqs
显示 IRQ 列表。
show_configs
显示现有配置文件列表。
what_is
提供有关所选实体的帮助。
GUI
启动图形用户界面(GUI)。

您可以使用 tuna -h 命令查看命令。对于每个命令,您可以使用 tuna < <command> -h 命令查看可选的参数。例如,使用 tuna isolate -h 命令,您可以查看 用于隔离 的选项。

21.3. 使用 tuna CLI 隔离 CPU

您可以使用 tuna CLI 将中断(IRQ)与不同专用 CPU 上的用户进程隔离,以最大程度降低实时环境中延迟。有关隔离 CPU 的更多信息,请参阅 中断和进程绑定

先决条件

  • tunapython-linux-procfs 软件包已安装。
  • 您在系统上具有 root 权限。

流程

  • 隔离一个或多个 CPU。

    # tuna isolate --cpus=<cpu_list>
    Copy to Clipboard

    cpu_list 是用逗号分开的列表或一系列要隔离的 CPU。

    例如:

    # tuna isolate --cpus=0,1
    Copy to Clipboard

    或者

    # tuna isolate --cpus=0-5
    Copy to Clipboard

21.4. 使用 tuna CLI 将中断移到指定的 CPU

您可以使用 tuna CLI 将中断(IRQ)移到专用 CPU,以便在实时环境中最小化或消除延迟。有关移动 IRQ 的更多信息,请参阅 中断和进程绑定

先决条件

  • tunapython-linux-procfs 软件包已安装。
  • 您在系统上具有 root 权限。

流程

  1. 列出将 IRQ 列表附加到的 CPU。

    # tuna show_irqs --irqs=<irq_list>
    Copy to Clipboard

    irq_list 是您要列出附加 CPU 的 IRQ 的逗号分隔列表。

    例如:

    # tuna show_irqs --irqs=128
    Copy to Clipboard
  2. 将 IRQ 列表附加到 CPU 列表。

    # tuna move --irqs=irq_list --cpus=<cpu_list>
    Copy to Clipboard

    irq_list 是您要附加的 IRQs 的逗号分隔列表,cpu_list 是要附加或一组 CPU 的以逗号分隔的 CPU 列表。

    例如:

    # tuna move --irqs=128 --cpus=3
    Copy to Clipboard

验证

  • 在将任何 IRQ 移到指定的 CPU 前和之后,比较所选 IRQ 的状态。

    # tuna show_irqs --irqs=128
    Copy to Clipboard

21.5. 使用 tuna CLI 更改进程调度策略和优先级

您可以使用 tuna CLI 更改进程调度策略和优先级。

先决条件

  • tunapython-linux-procfs 软件包已安装。
  • 您在系统上具有 root 权限。

    注意

    分配 OTHERBATCH 调度策略不需要 root 权限。

流程

  1. 查看线程的信息。

    # tuna show_threads --threads=<thread_list>
    Copy to Clipboard

    thread_list 是您要显示的进程的逗号分隔列表。

    例如:

    # tuna show_threads --threads=42369,42416,43859
    Copy to Clipboard
  2. 修改进程调度策略和线程的优先级。

    # tuna priority scheduling_policy:priority_number --threads=<thread_list>
    Copy to Clipboard
    • thread_list 是您要显示的调度策略和优先级的以逗号分隔的进程列表。
    • scheduling_policy 是以下之一:

      • 其他
      • BATCH
      • FIFO - First Out
      • RR - Round Robin
    • priority_number 是优先级号从 0 到 99,其中 0 没有优先级,99 是最高优先级。

      注意

      OTHERBATCH 调度策略不需要指定优先级。此外,唯一有效的优先级(如果指定)是 0。FIFORR 调度策略需要优先级 1 或更多。

      例如:

# tuna priority FIFO:1 --threads=42369,42416,43859
Copy to Clipboard

验证

  • 查看线程的信息,以确保信息更改。
# tuna show_threads --threads=42369,42416,43859
Copy to Clipboard

第 22 章 设置调度程序优先级

Red Hat Enterprise Linux for Real Time 内核允许对调度程序优先级进行精细的控制。它还允许将应用程序级别的程序调度到高于内核线程的优先级。

警告

设置调度程序优先级可能会导致系统变得无响应,或者如果关键内核进程无法根据需要运行,则行为不可预测。最终,正确的设置取决于工作负载。

22.1. 查看线程调度优先级

线程优先级使用一系列级别设置,范围从 0 ( 最低优先级)到 99 (最高优先级)。systemd 服务管理器可用于在内核启动后更改线程的默认优先级。

流程

  • 要查看正在运行的线程的调度优先级,请使用 tuna 工具:

    # tuna --show_threads
                          thread       ctxt_switches
        pid SCHED_ rtpri affinity voluntary nonvoluntary             cmd
      2      OTHER     0    0xfff       451            3        kthreadd
      3       FIFO     1        0     46395            2     ksoftirqd/0
      5      OTHER     0        0        11            1    kworker/0:0H
      7       FIFO    99        0         9            1   posixcputmr/0
      ...[output truncated]...
    Copy to Clipboard

22.2. 在引导过程中更改服务优先级

使用 systemd,您可以在引导过程中为启动的服务设置实时优先级。

单元配置指令用于在引导过程中更改服务的优先级。引导过程优先级更改通过使用 /etc/systemd/system/service.service.d/priority.conf 的 service 部分中的以下指令完成:

CPUSchedulingPolicy=

设置已执行进程的 CPU 调度策略。在 Linux 上获取一个调度类:

  • 其他
  • batch
  • idle
  • fifo
  • rr

CPUSchedulingPriority=

设置已执行进程的 CPU 调度优先级。可用的优先级范围取决于所选的 CPU 调度策略。对于实时调度策略,可以使用 1(最低优先级)和 99 (最高优先级)之间的整数。

先决条件

  • 有管理员特权。
  • 在启动时运行的服务。

流程

对于现有服务:

  1. 为服务创建补充的服务配置目录文件。

    # cat <<-EOF > /etc/systemd/system/mcelog.service.d/priority.conf
    Copy to Clipboard
  2. 将调度策略和优先级添加到 [Service] 部分中的 文件。

    例如:

    [Service]
    CPUSchedulingPolicy=fifo
    CPUSchedulingPriority=20
    EOF
    Copy to Clipboard
  3. 重新加载 systemd 脚本配置。

    # systemctl daemon-reload
    Copy to Clipboard
  4. 重启该服务。

    # systemctl restart mcelog
    Copy to Clipboard

验证

  • 显示服务的优先级。

    $ tuna -t mcelog -P
    Copy to Clipboard

    输出显示了服务配置的优先级。

    例如:

                        thread       ctxt_switches
      pid SCHED_ rtpri affinity voluntary nonvoluntary             cmd
    826     FIFO    20  0,1,2,3        13            0          mcelog
    Copy to Clipboard

22.3. 配置服务的 CPU 使用量

使用 systemd,您可以指定可在哪些服务上运行的 CPU。

先决条件

  • 有管理员特权。

流程

  1. 为服务创建补充的服务配置目录文件。

    # md sscd
    Copy to Clipboard
  2. 使用 [Service] 部分中的 CPUAffinity 属性将服务使用的 CPU 添加到 文件中。

    例如:

    [Service]
    CPUAffinity=0,1
    EOF
    Copy to Clipboard
  3. 重新加载 systemd 脚本配置。

    # systemctl daemon-reload
    Copy to Clipboard
  4. 重启该服务。

    # systemctl restart service
    Copy to Clipboard

验证

  • 显示指定服务仅限于的 CPU。

    $ tuna -t mcelog -P
    Copy to Clipboard

    其中 service 是指定的服务。

    以下输出显示 mcelog 服务仅限于 CPU 0 和 1。

                        thread       ctxt_switches
      pid SCHED_ rtpri affinity voluntary nonvoluntary             cmd
    12954   FIFO    20      0,1         2            1          mcelog
    Copy to Clipboard

22.4. 优先级映射

优先级在组中定义,有一些组专用于特定内核功能。对于实时调度策略,使用 1 (最低优先级)和 99 (最高优先级)之间的整数。

下表描述了优先级范围,可在设置进程的调度策略时使用。

表 22.1. 优先级范围的描述
Priority线程描述

1

低优先级内核线程

此优先级通常为需要超过 SCHED_OTHER 的任务保留。

2 - 49

可供使用

用于典型的应用程序优先级的范围。

50

默认 hard-IRQ 值

 

51 - 98

高优先级线程

对定期执行的线程使用此范围,且必须快速响应时间。不要将此范围用于 CPU 密集型线程,因为您将中断。

99

Watchdogs 和 migration

必须以最高优先级运行的系统线程。

第 23 章 网络确定性提示

TCP 可能会对延迟产生较大影响。TCP 增加延迟,以获取效率、控制拥塞并确保可靠交付。在调整时,请考虑以下点:

  • 您是否需要订购交付?
  • 您需要保护数据包丢失吗?

    传输超过一次的数据包可能会导致延迟。

  • 您需要使用 TCP?

    考虑通过在套接字中使用 TCP_NODELAY 来禁用 Nagle 缓冲算法。Nagle 算法收集小的传出数据包,以一次性发送所有,并可能会对延迟产生不利的影响。

23.1. 为延迟或吞吐量敏感的服务优化 RHEL

合并调优的目的是最小化给定工作负载所需的中断数量。在高吞吐量情况下,目标是有尽可能少的中断,同时保持高数据率。在低延迟情况下,可以使用更多中断来快速处理流量。

您可以调整网卡上的设置,以增加或减少组合到单个中断的数据包数。因此,您可以提高流量的吞吐量或延迟。

流程

  1. 识别遇到瓶颈的网络接口:

    # ethtool -S enp1s0
    NIC statistics:
         rx_packets: 1234
         tx_packets: 5678
         rx_bytes: 12345678
         tx_bytes: 87654321
         rx_errors: 0
         tx_errors: 0
         rx_missed: 0
         tx_dropped: 0
         coalesced_pkts: 0
         coalesced_events: 0
         coalesced_aborts: 0
    Copy to Clipboard

    识别在其名称中包含 dropdiscarderror 数据包计数器。这些特定统计测量网络接口卡(NIC)数据包缓冲区的实际数据包丢失,这可能是 NIC 合并造成的。

  2. 监控您在上一步中标识的数据包计数器的值。

    将它们与网络的期望值进行比较,以确定任何特定接口是否遇到了瓶颈。网络瓶颈的一些常见迹象包括但不限于:

    • 网络接口上的许多错误
    • 高数据包丢失
    • 网络接口的大量使用

      注意

      在识别网络瓶颈时,其他重要因素包括 CPU 使用率、内存使用率和磁盘 I/O。

  3. 检查当前的中断合并设置:

    # ethtool -c enp1s0
    Coalesce parameters for enp1s0:
            Adaptive RX: off
            Adaptive TX: off
            RX usecs: 100
            RX frames: 8
            RX usecs irq: 100
            RX frames irq: 8
            TX usecs: 100
            TX frames: 8
            TX usecs irq: 100
            TX frames irq: 8
    Copy to Clipboard
    • usecs 值指的是接收方或传送方在产生中断前等待的微秒数。
    • frames 值指的是接收方或传送方在产生中断前等待的帧数。
    • irq 值用于在网络接口已经处理中断时配置中断调节。

      注意

      不是所有网络接口卡都支持报告和更改示例输出中的所有值。

    • Adaptive RX/TX 值代表自适应中断合并机制,它动态调整中断合并设置。根据数据包条件,当 Adaptive RX/TX 启用时,NIC 驱动程序会自动计算合并值(每个 NIC 驱动程序的算法都不一样)。
  4. 根据需要修改合并设置。例如:

    • ethtool.coalesce-adaptive-rx 被禁用时,请将 ethtool.coalesce-rx-usecs 配置,来在产生中断前将 RX 数据包的延迟设为 100 微秒:

      # nmcli connection modify enp1s0 ethtool.coalesce-rx-usecs 100
      Copy to Clipboard
    • 启用 ethtool.coalesce-adaptive-rx,而 ethtool.coalesce-rx-usecs 设为其默认值:

      # nmcli connection modify enp1s0 ethtool.coalesce-adaptive-rx on
      Copy to Clipboard

      修改 Adaptive-RX 设置,如下所示:

      • 关注低延迟(低于 50us)的用户不应启用 Adaptive-RX
      • 关注吞吐量的用户可能启用 Adaptive-RX,而不会造成任何损害。如果他们不希望使用自适应中断合并机制,他们可以尝试将 ethtool.coalesce-rx-usecs 设置为大值,如 100us 或 250us 。
      • 在出现问题前,不确定其需求的用户不应修改此设置。
  5. 重新激活连接:

    # nmcli connection up enp1s0
    Copy to Clipboard

验证

  • 监控网络性能,并检查丢弃的数据包:

    # ethtool -S enp1s0
    NIC statistics:
         rx_packets: 1234
         tx_packets: 5678
         rx_bytes: 12345678
         tx_bytes: 87654321
         rx_errors: 0
         tx_errors: 0
         rx_missed: 0
         tx_dropped: 0
         coalesced_pkts: 12
         coalesced_events: 34
         coalesced_aborts: 56
    ...
    Copy to Clipboard

    rx_errorsrx_droppedtx_errorstx_dropped 字段的值应该为 0 或接近于 0 ,取决于网络流量和系统资源。这些字段中的高值表示有网络问题。您的计数器可以有不同的名称。严格监控其名称中包含"drop"、"discard"或"error"的数据包计数器。

    rx_packetstx_packetsrx_bytestx_bytes 的值应该随时间的推移而增加。如果值没有增加,则可能有网络问题。数据包计数器可以有不同的名称,具体取决于您的 NIC 驱动程序。

    重要

    ethtool 命令输出可能会因使用的 NIC 和驱动程序而变化。

    专注于非常低延迟的用户可以使用应用程序级指标或内核数据包时间戳 API 来进行监控。

23.2. 以太网网络的流控制

在以太网链路上,网络接口和交换机端口之间持续的数据传输可能会导致缓冲区容量满了。缓冲区容量满了会导致网络拥塞。在这种情况下,当发送方传输数据的速度高于接收方的处理能力时,可能会因为链接另一端(其是交换机端口)上网络接口的低数据处理能力而发生数据包丢失。

流控制机制管理以太网链路之间的数据传输,其中每个发送方和接收方都有不同的发送和接收能力。为避免数据包丢失,以太网流控制机制会临时暂停数据包传输,以便管理交换机端口的高传输率。请注意,交换机不会转发交换机端口之外的暂停帧。

当接收(RX)缓冲区变满时,接收方会向传送方发送暂停帧。然后,传送方会停止数据传输一个亚秒时间段,同时继续在此暂停期间缓冲传入的数据。此持续时间为接收方提供了足够时间来清空其接口缓冲区,并防止缓冲区溢出。

注意

以太网链接的任一端都可以向另一个端发送暂停帧。如果网络接口的接收缓冲区已满,网络接口将向交换机端口发送暂停帧。类似,当交换机端口的接收缓冲区已满时,交换机端口会向网络接口发送暂停帧。

默认情况下,Red Hat Enterprise Linux 中的大多数网络驱动程序都启用了暂停帧支持。要显示网络接口的当前设置,请输入:

# ethtool --show-pause enp1s0
Pause parameters for enp1s0:
...
RX:     on
TX:     on
...
Copy to Clipboard

与您的交换机厂商确认您的交换机是否支持暂停帧。

第 24 章 使用 trace-cmd 追踪延迟

trace-cmd 工具是 ftrace 工具的前端。通过使用 trace-cmd,您可以启用 ftrace 操作,而无需写入 /sys/kernel/debug/tracing/ 目录。trace-cmd 不会在安装时添加任何开销。

先决条件

  • 有管理员特权。

24.1. 安装 trace-cmd

trace-cmd 工具为 ftrace 工具提供了一个前端。

先决条件

  • 有管理员特权。

流程

  • 安装 trace-cmd 工具。

    # dnf install trace-cmd
    Copy to Clipboard

24.2. 运行 trace-cmd

您可以使用 trace-cmd 工具来访问所有 ftrace 功能。

先决条件

  • 有管理员特权。

流程

  • 输入 trace-cmd 命令

    其中 commandftrace 选项。

    注意

    有关命令和选项的完整列表,请参阅 trace-cmd (1) 手册页。大多数命令也具有自己的 man page trace-cmd-命令

24.3. trace-cmd 示例

命令示例演示了如何使用 trace-cmd 工具跟踪内核功能。

例子

  • myapp 运行时,启用并启动在内核中执行的记录功能。

    # trace-cmd record -p function myapp
    Copy to Clipboard

    这记录了所有 CPU 和所有任务的功能,即使与 myapp 无关。

  • 显示结果。

    # trace-cmd report
    Copy to Clipboard
  • 仅记录在 myapp 运行时以 sched 开头的功能。

    # trace-cmd record -p function -l 'sched*' myapp
    Copy to Clipboard
  • 启用所有 IRQ 事件。

    # trace-cmd start -e irq
    Copy to Clipboard
  • 启动 wakeup_rt tracer。

    # trace-cmd start -p wakeup_rt
    Copy to Clipboard
  • 在禁用功能追踪时启动 preemptirqsoff tracer。

    # trace-cmd start -p preemptirqsoff -d
    Copy to Clipboard
    注意

    RHEL 8 中的 trace-cmd 版本关闭 ftrace_enabled,而不是使用 function-trace 选项。您可以使用 trace-cmd start -p 功能再次启用 ftrace

  • 恢复在 trace-cmd 启动修改前系统所处的状态。

    # trace-cmd start -p nop
    Copy to Clipboard

    如果要在使用 trace-cmd 后使用 debugfs 文件系统(无论是在 meantime 中重启系统),这是很重要的。

  • 跟踪单个追踪点。

    # trace-cmd record -e sched_wakeup ls /bin
    Copy to Clipboard
  • 停止追踪。

    # trace-cmd record stop
    Copy to Clipboard

第 25 章 使用 tuned-profiles-real-time 隔离 CPU

要为应用程序线程提供最可能执行时间,您可以隔离 CPU。因此,尽可能从 CPU 中删除尽可能多的任务。隔离 CPU 通常涉及:

  • 删除所有用户空间线程。
  • 删除所有 unbound 内核线程。与内核相关的绑定线程链接到特定 CPU,且无法移动。
  • 通过修改系统中每个中断请求(IRQ)编号 N/proc/irq/N/smp_affinity 属性来删除中断。

通过使用 tuned-profiles-realtime 软件包的 isolated_cores=cpulist 配置选项,您可以自动执行操作来隔离 CPU。

先决条件

  • 有管理员特权。

25.1. 选择要隔离的 CPU

选择要隔离的 CPU 需要仔细考虑系统的 CPU 拓扑。不同的用例需要不同的配置:

  • 如果您有一个多线程应用程序,线程需要通过共享缓存相互通信,则需要在同一 NUMA 节点或物理套接字上保留它们。
  • 如果您运行多个不相关的实时应用程序,请将 CPU 与 NUMA 节点或套接字分离。

hwloc 软件包提供用于获取 CPU 信息的工具,包括 lstopo-no-graphicsnumactl

先决条件

  • 已安装 hwloc 软件包。

流程

  1. 查看物理软件包中可用 CPU 的布局:

    # lstopo-no-graphics --no-io --no-legend --of txt
    Copy to Clipboard

    图 25.1. 使用 lstopo-no-graphics 显示 CPU 布局

    lstopo 没有图形输出

    此命令对多线程应用程序很有用,因为它显示有多少内核和套接字可用,以及 NUMA 节点的逻辑距离。

    另外,hwloc-gui 软件包提供了 lstopo 工具,它生成图形输出。

  2. 查看 CPU 的更多信息,如节点之间的距离:

    # numactl --hardware
    available: 2 nodes (0-1)
    node 0 cpus: 0 1 2 3
    node 0 size: 16159 MB
    node 0 free: 6323 MB
    node 1 cpus: 4 5 6 7
    node 1 size: 16384 MB
    node 1 free: 10289 MB
    node distances:
    node   0   1
      0:  10  21
      1:  21  10
    Copy to Clipboard

25.2. 使用 TuneD 的 isolated_cores 选项隔离 CPU

隔离 CPU 的初始机制是在内核引导命令行中指定 boot 参数 isolcpus=cpulist。为 RHEL for Real Time 执行此操作的建议方法是使用 TuneD 守护进程及其 tuned-profiles-realtime 软件包。

注意

tuned-profiles-realtime 版本 2.19 及更高版本中,内置函数 calc_isolated_cores 会自动应用初始 CPU 设置。/etc/tuned/realtime-variables.conf 配置文件包含默认变量内容为 isolated_cores=${f:calc_isolated_cores:2}

默认情况下,calc_isolated_cores 为每个插槽保留一个内核,用于内务处理并隔离其余内核。如果必须更改默认配置,请在 /etc/tuned/realtime-variables.conf 配置文件中注释掉 isolated_cores=${f:calc_isolated_cores:2} 行,并按照流程步骤使用 TuneD 的 isolated_cores 选项隔离 CPU。

先决条件

  • TuneDtuned-profiles-realtime 软件包已安装。
  • 您在系统上具有 root 权限。

流程

  1. 以 root 用户身份,在文本编辑器中打开 /etc/tuned/realtime-variables.conf
  2. 设置 isolated_cores=cpulist,以指定您要隔离的 CPU。您可以使用 CPU 编号和范围。

    示例:

    isolated_cores=0-3,5,7
    Copy to Clipboard

    这将隔离内核 0、1、2、3、5 和 7。

    在有 8 个内核的双插槽系统中,其中 NUMA 节点 0-3 和 NUMA 节点 1 具有内核 4-8,要为多线程应用程序分配两个内核,请指定:

    isolated_cores=4,5
    Copy to Clipboard

    这可防止任何用户空间线程分配给 CPU 4 和 5。

    要为不相关的应用程序从不同的 NUMA 节点选择 CPU,请指定:

    isolated_cores=0,4
    Copy to Clipboard

    这可防止任何用户空间线程分配给 CPU 0 和 4。

  3. 使用 tuned-adm 工具激活实时 TuneD 配置集。

    # tuned-adm profile realtime
    Copy to Clipboard
  4. 重启机器以使更改生效。

验证

  • 在内核命令行中搜索 isolcpus 参数:

    $ cat /proc/cmdline | grep isolcpus
    BOOT_IMAGE=/vmlinuz-6.12.0-55.9.1.el10_0.x86_64 root=/dev/mapper/rhel_foo-root ro crashkernel=auto rd.lvm.lv=rhel_foo/root rd.lvm.lv=rhel_foo/swap console=ttyS0,115200n81 isolcpus=0,4
    Copy to Clipboard

25.3. 使用 nohz 和 nohz_full 参数隔离 CPU

nohznohz_full 参数修改指定 CPU 上的活动。要启用这些内核引导参数,您需要使用以下 TuneD 配置集之一: realtime-virtual-hostrealtime-virtual-guestcpu-partitioning

nohz=on

减少特定 CPU 集合上的计时器活动。

nohz 参数主要用于减少空闲 CPU 上的计时器中断。这有助于通过允许空闲 CPU 以降低电源模式运行。虽然 nohz 参数不直接用于实时响应时间,但 nohz 参数不会直接影响实时响应时间。但是,需要 nohz 参数激活 nohz_full 参数,该参数对实时性能有积极影响。

nohz_full=cpulist
nohz_full 参数会以不同的方式对待指定 CPU 列表中的计时器 tick。如果将 CPU 指定为 nohz_full CPU,且 CPU 中只有一个可运行任务,则内核将停止向该 CPU 发送计时器 tick。因此,运行应用程序可能会花费更多时间,减少服务中断和上下文切换的时间。

第 26 章 限制 SCHED_OTHER 任务迁移

您可以使用 sched_nr_migrate 变量限制 SCHED_OTHER 迁移到其他 CPU 的任务。

先决条件

  • 有管理员特权。

26.1. 任务迁移

如果 SCHED_OTHER 任务生成大量其他任务,则它们将在同一 CPU 上运行。迁移 任务或 softirq 将尝试平衡这些任务,以便它们可以在空闲的 CPU 上运行。

可以调整 sched_nr_migrate 选项,以指定每次要移动的任务数量。由于实时任务有不同的迁移方式,因此它们不会直接受到此问题的影响。但是,当 softirq 移动任务时,它会锁定运行队列 spinlock,从而禁用中断。

如果需要移动大量任务,它会在中断被禁用时发生,因此不允许同时发生计时器事件或唤醒。当 sched_nr_migrate 设置为一个大的值时,这可能会导致实时任务出现严重延迟。

26.2. 使用 sched_nr_migrate 变量限制 SCHED_OTHER 任务迁移

增加 sched_nr_migrate 变量提供来自 SCHED_OTHER 线程的高性能,这些线程会生成大量任务,但会牺牲实时延迟。

对于 SCHED_OTHER 任务性能的低实时任务延迟,必须降低该值。默认值为 8

流程

  • 要调整 sched_nr_migrate 变量的值,请将值直接传给 /proc/sys/kernel/sched_nr_migrate

    # echo 2 > /proc/sys/kernel/sched_nr_migrate
    Copy to Clipboard

验证

  • 查看 /proc/sys/kernel/sched_nr_migrate 的内容:

    # cat > /proc/sys/kernel/sched_nr_migrate
    2
    Copy to Clipboard

第 27 章 减少 TCP 性能激增

生成 TCP 时间戳可能会导致 TCP 性能激增。sysctl 命令控制 TCP 相关条目的值,设置位于 /proc/sys/net/ipv4/tcp_timestamps 的时间戳内核参数。

先决条件

  • 有管理员特权。

27.1. 关闭 TCP 时间戳

关闭 TCP 时间戳可减少 TCP 性能激增。

流程

  • 关闭 TCP 时间戳:

    # sysctl -w net.ipv4.tcp_timestamps=0
    net.ipv4.tcp_timestamps = 0
    Copy to Clipboard

    输出显示 net.ip4.tcp_timestamps 选项的值是 0。也就是说,TCP 时间戳被禁用。

27.2. 打开 TCP 时间戳

生成时间戳可能会导致 TCP 性能激增。您可以通过禁用 TCP 时间戳来降低 TCP 性能激增。如果您发现生成 TCP 时间戳没有导致 TCP 性能激增,您可以启用它们。

流程

  • 启用 TCP 时间戳。

    # sysctl -w net.ipv4.tcp_timestamps=1
    net.ipv4.tcp_timestamps = 1
    Copy to Clipboard

    输出显示 net.ip4.tcp_timestamps 的值为 1。也就是说,启用了 TCP 时间戳。

27.3. 显示 TCP 时间戳状态

您可以查看 TCP 时间戳生成的状态。

流程

  • 显示 TCP 时间戳生成状态:

    # sysctl net.ipv4.tcp_timestamps
    net.ipv4.tcp_timestamps = 0
    Copy to Clipboard

    1 表示正在生成时间戳。值 0 表示没有生成时间戳。

第 28 章 使用 RCU 回调提高 CPU 性能

Read-Copy-Update (RCU)系统是一个无锁定机制,用于内核中线程的相互排除。由于执行 RCU 操作,在删除内存安全时,调用-backs 有时会在 CPU 上排队。

使用 RCU 回调提高 CPU 性能:

  • 您可以从 Operator 中删除 CPU 来运行 CPU 回调。
  • 您可以分配一个 CPU 来处理所有 RCU 回调。此 CPU 称为内务 CPU。
  • 您可以从 RCU 卸载线程的责任中减轻 CPU。

这个组合减少了专用于用户工作负载的 CPU 的不同影响。

先决条件

  • 有管理员特权。
  • tuna 软件包已安装

28.1. 卸载 RCU 回调

您可以使用 rcu_nocbsrcu_nocb_poll 内核参数卸载 RCU 回调。

流程

  • 要从运行 RCU 回调的候选中删除一个或多个 CPU,请在 rcu_nocbs 内核参数中指定 CPU 列表,例如:

    rcu_nocbs=1,4-6
    Copy to Clipboard

    或者

    rcu_nocbs=3
    Copy to Clipboard

    第二个示例指示了 CPU 3 是一个 no-callback CPU 的内核。这意味着,RCU 回调不会在 rcuc/$CPU 线程固定到 CPU 3 中,而是在 rcuo/$CPU 线程中进行。您可以将此 trhead 移到内务 CPU,以减轻 CPU 3 从被分配的 RCU 回调作业。

28.2. 移动 RCU 回调

您可以分配一个内务处理 CPU 来处理所有 RCU 回调线程。要做到这一点,请使用 tuna 命令,并将所有 RCU 回调移到内务 CPU。

流程

  • 将 RCU 回调线程移到 housekeeping CPU 中:

    # tuna --threads=rcu --cpus=x --move
    Copy to Clipboard

    其中 x 是内务 CPU 的 CPU 号。

此操作减轻 CPU X 以外的所有 CPU 处理 RCU 回调线程。

28.3. 从 RCU 卸载线程中减轻 CPU

虽然 RCU 卸载线程可以在另一个 CPU 上执行 RCU 回调,但每个 CPU 负责处理对应的 RCU 卸载线程。您可以减轻这个职责的 CPU,

流程

  • 设置 rcu_nocb_poll 内核参数。

    这个命令会导致计时器定期引发 RCU 卸载线程,以检查是否有回调在运行。

第 29 章 使用 ftrace 追踪延迟

ftrace 工具是 RHEL for Real Time 内核提供的诊断工具之一。ftrace 可以被开发人员用来分析和调试用户空间外发生的延迟和性能问题。ftrace 工具具有各种选项,允许您以不同的方式使用实用程序。它可用于跟踪上下文切换,测量高优先级任务启动所需的时间、中断的长度被禁用,或者列出给定期间执行的所有内核功能。

一些 ftrace tracers (如功能 tracer)可能会导致大量数据生成,这可能会将 trace 日志分析转换为耗时的任务。但是,您可以指示 tracer 仅在应用程序到达关键代码路径时开始和结束。

先决条件

  • 有管理员特权。

29.1. 使用 ftrace 工具来跟踪延迟

您可以使用 ftrace 工具跟踪延迟。

流程

  1. 查看系统上可用的 tracer。

    # cat /sys/kernel/debug/tracing/available_tracers
    function_graph wakeup_rt wakeup preemptirqsoff preemptoff irqsoff function nop
    Copy to Clipboard

    ftrace 的用户界面是 debugfs 中的一系列文件。

    ftrace 文件也位于 /sys/kernel/debug/tracing/ 目录中。

  2. 移到 /sys/kernel/debug/tracing/ 目录。

    # cd /sys/kernel/debug/tracing
    Copy to Clipboard

    此目录中的文件只能由 root 用户修改,因为启用追踪可能会影响系统性能。

  3. 启动追踪会话:

    1. /sys/kernel/debug/tracing/available_tracers 中的可用 tracer 列表中选择要使用的 tracer。
    2. 将选择器的名称插入到 /sys/kernel/debug/tracing/current_tracer 中。

      # echo preemptoff > /sys/kernel/debug/tracing/current_tracer
      Copy to Clipboard
      注意

      如果您将单个 '>' 与 echo 命令搭配使用,它将覆盖文件中的任何现有值。如果要将值附加到文件,请使用 '>>'。

  4. function-trace 选项很有用,因为使用 wakeup_rtpreemptirqsoff 的追踪延迟,因此自动启用 函数追踪 (这可能会增加开销)。

    检查是否启用了 functionfunction_graph tracing:

    # cat /sys/kernel/debug/tracing/options/function-trace
    1
    Copy to Clipboard
    • 1 表示启用了 functionfunction_graph tracing。
    • 0 表示禁用 functionfunction_graph tracing。
  5. 默认情况下启用 functionfunction_graph tracing。要打开或关闭函数和 function _graph tracing,请将适当的值传给 /sys/kernel/debug/tracing/options/function-trace 文件。

    # echo 0 > /sys/kernel/debug/tracing/options/function-trace
    # echo 1 > /sys/kernel/debug/tracing/options/function-trace
    Copy to Clipboard
    重要

    使用 echo 命令时,请确保在值和 > 字符之间放置一个空格字符。 在 shell 提示符下,使用 0& gt ;、1 & gt; 和 2> (没有空格字符)指的是标准输入、标准输出和标准错误。错误地使用它们可能会导致意外的追踪输出。

  6. 通过更改 /debugfs/tracing/ 目录中各种文件的值来调整 tracer 的详细信息和参数。

    例如:

    irqsoffpreemptoffpreempirqsoffwakeup tracer 持续监控延迟。当它们记录大于 tracing_max_latency 中记录的延迟时,会记录该延迟的 trace,tracing_max_latency 会更新为新的最长时间。这样,trace _max_latency 始终显示记录最高的延迟,因为它是上次重置的。

    • 要重置最大延迟,请在 tracing_max_latency 文件中回显 0

      # echo 0 > /sys/kernel/debug/tracing/tracing_max_latency
      Copy to Clipboard
    • 要查看大于集合数量的延迟,以微秒为单位回显量:

      # echo 200 > /sys/kernel/debug/tracing/tracing_max_latency
      Copy to Clipboard

      当设置追踪阈值时,它会覆盖最大延迟设置。当记录大于阈值的延迟时,无论最大延迟是什么,都会记录它。查看 trace 文件时,只会显示最后记录的延迟。

    • 要设置阈值,请回显必须记录延迟的微秒数:

      # echo 200 > /sys/kernel/debug/tracing/tracing_thresh
      Copy to Clipboard
  7. 查看追踪日志:

    # cat /sys/kernel/debug/tracing/trace
    Copy to Clipboard
  8. 要存储 trace 日志,请将它们复制到另一个文件中:

    # cat /sys/kernel/debug/tracing/trace > /tmp/lat_trace_log
    Copy to Clipboard
  9. 查看要追踪的功能:

    # cat /sys/kernel/debug/tracing/set_ftrace_filter
    Copy to Clipboard
  10. 通过编辑 /sys/kernel/debug/tracing/set_ftrace_filter 中的设置来过滤要追踪的功能。如果在文件中没有指定过滤器,则会跟踪所有功能。
  11. 要更改过滤器设置,请回显要追踪的功能的名称。过滤器允许在搜索词的开头或结尾使用 '*' 通配符。

    例如,请参阅 ftrace 示例

29.2. ftrace 文件

以下是 /sys/kernel/debug/ 目录中的主要文件。

ftrace 文件

trace
显示 ftrace 跟踪输出的文件。这实际上是一个 trace 的快照,因为当该文件读取时,trace 会停止,且不会消耗读取的事件。也就是说,如果用户禁用追踪并读取此文件,它将在每次读取时都报告相同的内容。
trace_pipe
在读取 trace live 时显示 ftrace trace 的输出的文件。它是 producer/consumer 追踪。也就是说,每个读取都将消耗读取的事件。这可用于读取活跃的 trace,而不会在读取时停止 trace。
available_tracers
已编译到内核中的 ftrace tracer 列表。
current_tracer
启用或禁用 ftrace tracer。
events
包含要跟踪的事件的目录,可用于启用或禁用事件,以及为事件设置过滤器。
tracing_on
禁用并启用对 ftrace 缓冲区的记录。通过 tracing_on 文件禁用追踪不会禁用内核内发生的实际追踪。它只禁用写入缓冲区。仍然发生 trace 的工作,但数据不会在任何位置出现。

29.3. ftrace tracers

根据内核的配置方式,并非所有 tracer 都可能可用于给定的内核。对于 RHEL for Real Time 内核,trace 和 debug 内核具有与生产内核不同的 tracer。这是因为当 tracer 配置为内核中,但未激活时,一些 tracers 有显著的开销。这些 tracer 仅针对 tracedebug 内核启用。

tracers

function
一个最广泛适用的 tracer。跟踪内核中的功能调用。这可能导致明显的开销,具体取决于跟踪的功能数量。如果没有激活,则会产生较少的开销。
function_graph

function_graph tracer 旨在以更视觉的方式显示结果。这个 tracer 还跟踪函数的退出,显示内核中函数调用的流。

注意

当启用时,这个 tracer 的开销比 函数 tracer 的更多开销,但在禁用时的开销相同。

Wakeup
报告所有 CPU 间发生活动的完整 CPU 追踪器。它记录系统中唤醒最高优先级任务所需的时间,无论任务是实时任务。记录唤醒非实时任务所需的时间会隐藏唤醒实时任务所需的时间。
wakeup_rt
报告所有 CPU 间发生活动的完整 CPU 追踪器。它记录从当前最高优先级任务中获取的时间,以唤醒直到调度时间为止。此 tracer 仅记录实时任务的时间。
preemptirqsoff
跟踪禁用抢占或中断的区域,并记录禁用抢占或中断的最大时间。
preemptoff
与 preemptirqsoff 追踪程序类似,但只跟踪禁用了 pre-emption 的最大间隔。
irqsoff
与 preemptirqsoff 追踪程序类似,但只跟踪禁用中断的最大间隔。
nop
默认 tracer。它不提供任何追踪功能本身,但随着事件可能交入任何 tracer,nop tracer 用于追踪事件。

29.4. ftrace 示例

以下提供了一些更改要跟踪的功能过滤的示例。您可以在单词的开头和结尾使用 * 通配符。例如 :*irq\* 将选择名称中包含 irq 的所有功能。但是,通配符不能在单词内使用。

以双引号实施搜索词和通配符字符可确保 shell 不会尝试将搜索扩展到当前的工作目录。

过滤器示例

  • 仅追踪 调度功能

    # echo schedule > /sys/kernel/debug/tracing/set_ftrace_filter
    Copy to Clipboard
  • 跟踪以 锁定结尾的所有功能

    # echo "*lock" > /sys/kernel/debug/tracing/set_ftrace_filter
    Copy to Clipboard
  • 跟踪以 spin_ 开头的所有功能:

    # echo "spin_*" > /sys/kernel/debug/tracing/set_ftrace_filter
    Copy to Clipboard
  • 跟踪名称中 cpu 的所有功能:

    # echo "cpu" > /sys/kernel/debug/tracing/set_ftrace_filter
    Copy to Clipboard

第 30 章 应用程序时间戳

执行频繁时间戳的应用程序会受到读取时钟的 CPU 成本的影响。用于读取时钟的高成本和时间可能会对应用程序的性能造成负面影响。

您可以通过选择一个具有读机制的硬件时钟来降低读取时钟的成本,比默认时钟更快。

在 RHEL for Real Time 中,可以使用带有 clock_gettime () 函数的 POSIX 时钟来获取更多性能,以生成可能最低 CPU 成本的时钟读取。

这些好处对于使用带有高读取成本的硬件时钟的系统更为明显。

30.1. POSIX 时钟

POSIX 是实施和代表时间源的标准。您可以为应用程序分配 POSIX 时钟,而不影响系统中的其他应用程序。这与内核选择并在系统中实施的硬件时钟相反。

用于读取给定 POSIX 时钟的函数是 clock_gettime (),它在 < time.h > 中定义。内核与 clock_gettime () 对应的是系统调用。当用户进程调用 clock_gettime () 时:

  1. 对应的 C 库(glibc)调用 sys_clock_gettime () 系统调用。
  2. sys_clock_gettime () 执行请求的操作。
  3. sys_clock_gettime () 将结果返回给用户程序。

但是,从用户应用程序切换到内核的上下文具有 CPU 成本。虽然这个成本非常低,但如果操作重复了数千次,但累积的成本可能会对应用程序的整体性能产生影响。为了避免上下文切换到内核,从而更快地读取时钟,支持 CLOCK_MONOTONIC_COARSECLOCK_REALTIME_COARSE POSIX 时钟,格式为虚拟动态共享对象(VDSO)库函数。

使用其中一个 _COARSE 时钟变体执行 clock_gettime () 的时间读取不需要内核干预,且完全在用户空间中执行。这会显著提高性能。_COARSE 时钟读取的时间具有毫秒(ms)解析,这意味着不会记录小于 1 ms 的时间间隔。POSIX 时钟的 _COARSE 变体适合可容纳 millisecond 时钟分辨率的任何应用程序。

30.2. clock_gettime 中的 _COARSE 时钟变体

示例代码输出显示将 clock_gettime 功能与 CLOCK_MONOTONIC_COARSE POSIX 时钟一起使用。

#include <time.h>

main()
{
	int rc;
	long i;
	struct timespec ts;

	for(i=0; i<10000000; i++) {
		rc = clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
	}
}
Copy to Clipboard

您可以改进上面的示例,添加检查来验证 clock_gettime() 的返回值,验证 rc 变量的值,或确保 ts 结构的内容被信任。

注意

clock_gettime () man page 提供了有关编写更可靠的应用程序的更多信息。

重要

使用 clock_gettime() 函数的程序必须通过将 -lrt 添加到 gcc 命令行来与 rt 库相关联。

$ gcc clock_timing.c -o clock_timing -lrt

第 31 章 使用 TCP_NODELAY 提高网络延迟

默认情况下,TCP 使用 Nagle 的算法收集小传出数据包,以一次性发送所有。这可能导致更高的延迟率。

先决条件

  • 有管理员特权。

31.1. 使用 TCP_NODELAY 的影响

对发送的每个数据包需要低延迟的应用程序必须在启用了 TCP_NODELAY 选项的套接字上运行。这会在事件发生时立即向内核发送缓冲区写入。

备注
要使 TCP_NODELAY 有效,应用程序必须避免在逻辑上相关的缓冲区写入。否则,这些小写入会导致 TCP 将这些多个缓冲区作为单个数据包发送,从而导致整体性能不佳。

如果应用程序有几个与逻辑相关的缓冲,且必须作为一个数据包发送,请应用以下临时解决方案之一以避免性能不佳:

  • 在内存中构建连续数据包,然后在配置了 TCP _NODELAY 的套接字上将逻辑数据包发送到 TCP。
  • 创建 I/O 向量,并在配置了 TCP_NODELAY 的套接字中使用 writev 命令将它传递给内核。
  • 使用 TCP_CORK 选项。TCP_CORK 告知 TCP 在发送任何数据包前等待应用删除 cork。此命令会导致它接收的缓冲区附加到现有缓冲区中。这允许应用程序在内核空间中构建数据包,在使用为层提供抽象的不同库时,可能需要该数据包。

当应用程序中的每个组件在内核中构建逻辑数据包时,该套接字应该被取消封锁,允许 TCP 立即发送累积逻辑数据包。

31.2. 启用 TCP_NODELAY

TCP_NODELAY 选项会在事件发生时向内核发送缓冲区写入,且无延迟。使用 setsockopt () 函数启用 TCP_NODELAY

流程

  1. 将以下行添加到 TCP 应用的 .c 文件中:

    int one = 1;
    setsockopt(descriptor, SOL_TCP, TCP_NODELAY, &one, sizeof(one));
    Copy to Clipboard
  2. 保存文件并退出编辑器。
  3. 应用以下临时解决方案之一以防止性能不佳。

    • 在内存中构建连续数据包,然后在配置了 TCP _NODELAY 的套接字上将逻辑数据包发送到 TCP。
    • 创建 I/O 向量,并在配置了 TCP_NODELAY 的套接字中使用 writev 将其传递给内核。

31.3. 启用 TCP_CORK

TCP_CORK 选项可防止 TCP 发送任何数据包,直到套接字"不corked"。

流程

  1. 将以下行添加到 TCP 应用的 .c 文件中:

    int one = 1;
    setsockopt(descriptor, SOL_TCP, TCP_CORK, &one, sizeof(one));
    Copy to Clipboard
  2. 保存文件并退出编辑器。
  3. 在由应用中各种组件在内核中构建了逻辑数据包后,禁用 TCP_CORK

    int zero = 0;
    setsockopt(descriptor, SOL_TCP, TCP_CORK, &zero, sizeof(zero));
    Copy to Clipboard

    TCP 立即发送累积逻辑数据包,而不等待来自应用程序的任何进一步数据包。

第 32 章 使用 mutex 防止资源过度使用

相互排除(mutex)算法用于防止过度使用常见资源。

32.1. mutex 选项

相互排除(mutex)算法用于防止使用通用资源的进程。快速用户空间 mutex (futex)是一个工具,它允许用户空间线程在不需要上下文切换到内核空间的情况下声明 mutex,只要 mutex 尚未由另一个线程保留。

当您使用标准属性初始化 pthread_mutex_t 对象时,会创建私有、非递归、非繁忙和非优先级继承功能。这个对象不提供 pthreads API 和 RHEL for Real Time 内核提供的任何 benfits。

要从 pthreads API 和 RHEL for Real Time 内核中受益,请创建一个 pthread_mutexattr_t 对象。此对象存储为 futex 定义的属性。

注意

术语 futexmutex 用于描述 POSIX 线程(pthread) mutex 构造。

32.2. 创建 mutex 属性对象

要为 mutex 定义任何其他功能,请创建一个 pthread_mutexattr_t 对象。此对象存储 futex 的定义属性。这是您必须始终执行的基本安全流程。

流程

  • 使用以下任一创建 mutex 属性对象:

    • pthread_mutex_t(my_mutex);
    • pthread_mutexattr_t(&my_mutex_attr);
    • pthread_mutexattr_init(&my_mutex_attr);

有关高级 mutex 属性的更多信息,请参阅高级 mutex 属性

32.3. 使用标准属性创建 mutex

当您使用标准属性初始化 pthread_mutex_t 对象时,会创建私有、非递归、非繁忙和非优先级继承功能。

流程

  • 使用以下命令之一在 pthreads 下创建一个 mutex 对象:

    • pthread_mutex_t(my_mutex);
    • pthread_mutex_init(&my_mutex, &my_mutex_attr);

      其中 &my_mutex_attr; 是一个 mutex 属性对象。

32.4. 高级 mutex 属性

以下高级 mutex 属性可以存储在 mutex 属性对象中:

mutex 属性

共享和私有 mutexes

共享 mutexes 可以在进程之间使用,但它们可能会带来更多的开销。

pthread_mutexattr_setpshared(&my_mutex_attr, PTHREAD_PROCESS_SHARED);

实时优先级继承

您可以使用优先级继承来避免对优先级进行优先级的问题。

pthread_mutexattr_setprotocol(&my_mutex_attr, PTHREAD_PRIO_INHERIT);

强大的互斥器

当 pthread dies 时,会发布 pthread 下的强大的 mutexes。但是,这会产生较高的开销。此字符串中的 _NP 表示这个选项是非 POSIX 或不可移植。

pthread_mutexattr_setrobust_np(&my_mutex_attr, PTHREAD_MUTEX_ROBUST_NP);

mutex 初始化

共享 mutexes 可以在进程之间使用,但它们可能会带来更多的开销。

pthread_mutex_init(&my_mutex_attr, &my_mutex);

32.5. 清理 mutex 属性对象

在使用 mutex 属性对象创建 mutex 后,您可以保留属性对象来初始化同一类型的 mutexes,或者您可以清理它。mutex 在这两种情况下都不会受到影响。

流程

  • 使用 pthread_mutexattr_destroy () 函数清理属性对象:

    pthread_mutexattr_destroy(&my_mutex_attr);
    Copy to Clipboard

    mutex 现在作为常规的 pthread_mutex 运行,并可正常锁定、解锁和销毁。

第 33 章 分析应用程序性能

perf 是一个性能分析工具。它提供简单的命令行界面,并提取 Linux 性能测量的 CPU 硬件差异。perf 基于内核导出的 perf_events 接口。

perf 的一个优点是它既是内核和构架中立。可以在不需要特定系统配置的情况下检查分析数据。

先决条件

  • perf 软件包必须安装在系统上。
  • 有管理员特权。

33.1. 收集系统范围统计信息

perf record 命令用于收集系统范围的统计信息。它可用于所有处理器。

流程

  • 收集系统范围的性能统计信息。

    # perf record -a
    ^C[ perf record: Woken up 1 times to write data ]
    [ perf record: Captured and wrote 0.725 MB perf.data (~31655 samples) ]
    Copy to Clipboard

    在本例中,所有 CPU 使用 -a 选项表示,进程在几秒钟后被终止。结果显示它收集 0.725 MB 的数据并将其保存到新创建的 perf.data 文件中。

验证

  • 确保已创建结果文件。

    # ls
    perf.data
    Copy to Clipboard

33.2. 归档性能分析结果

您可以使用 perf archive 命令分析其他系统上 perf 的结果。如果出现以下情况,可能不需要这样做:

  • 分析系统中已存在动态共享对象(DSO),如二进制文件和库,如 ~/.debug/ 缓存。
  • 这两个系统都有相同的二进制文件集合。

流程

  1. perf 命令创建结果的存档。

    # perf archive
    Copy to Clipboard
  2. 从归档创建 tarball。

    # tar cvf perf.data.tar.bz2 -C ~/.debug
    Copy to Clipboard

33.3. 分析性能分析结果

现在,可以使用 perf report 命令直接调查 perf record 功能中的数据。

流程

  • 直接从 perf.data 文件或从归档的 tarball 分析结果。

    # perf report
    Copy to Clipboard

    报告的输出按照应用程序的最大 CPU 使用量百分比进行排序。它显示了示例是否在进程的内核或用户空间中发生。

    报告显示有关从中获取示例的模块信息:

    • 未放入内核模块的内核示例使用表示法 [kernel.kallsyms] 进行标记。
    • 在内核模块中发生的内核示例被标记为 [module], [ext4]
    • 对于用户空间中的进程,结果可能会显示与进程关联的共享库。

      报告指示进程是否也发生在内核或用户空间中。

    • 结果 [.] 表示用户空间。
    • 结果 [k] 表示内核空间。

    可以审核更精细的细节,包括适合经验丰富的 perf 开发人员的数据。

33.4. 列出预定义的事件

有一系列可用选项来获取硬件追踪点活动。

流程

  • 列出预定义的硬件和软件事件:

    # perf list
    List of pre-defined events (to be used in -e):
      cpu-cycles OR cycles                               [Hardware event]
      stalled-cycles-frontend OR idle-cycles-frontend    [Hardware event]
      stalled-cycles-backend OR idle-cycles-backend      [Hardware event]
      instructions                                       [Hardware event]
      cache-references                                   [Hardware event]
      cache-misses                                       [Hardware event]
      branch-instructions OR branches                    [Hardware event]
      branch-misses                                      [Hardware event]
      bus-cycles                                         [Hardware event]
    
      cpu-clock                                          [Software event]
      task-clock                                         [Software event]
      page-faults OR faults                              [Software event]
      minor-faults                                       [Software event]
      major-faults                                       [Software event]
      context-switches OR cs                             [Software event]
      cpu-migrations OR migrations                       [Software event]
      alignment-faults                                   [Software event]
      emulation-faults                                   [Software event]
      ...[output truncated]...
    Copy to Clipboard

33.5. 获取指定事件的统计信息

您可以使用 perf stat 命令查看特定的事件。

流程

  1. 使用 perf stat 功能查看上下文切换数量:

    # perf stat -e context-switches -a sleep 5
    Performance counter stats for 'sleep 5':
    
                15,619 context-switches
    
           5.002060064 seconds time elapsed
    Copy to Clipboard

    结果显示 5 秒,15619 上下文切换发生。

  2. 通过运行脚本来查看文件系统活动。下面显示了一个示例脚本:

    # for i in {1..100}; do touch /tmp/$i; sleep 1; done
    Copy to Clipboard
  3. 在另一个终端中运行 perf stat 命令:

    # perf stat -e ext4:ext4_request_inode -a sleep 5
     Performance counter stats for 'sleep 5':
    
                     5 ext4:ext4_request_inode
    
           5.002253620 seconds time elapsed
    Copy to Clipboard

    结果显示,脚本在 5 秒内需要创建 5 个文件,表示存在 5 个 索引节点 请求。

第 34 章 使用压力测试实时系统

stress-ng 工具测量系统的能力,以便在不可尝试的情况下保持良好的效率水平。stress-ng 工具是一种压力工作负载生成器,用于加载和压力所有内核接口。它包括广泛的压力机制,称为压力者。压力测试使得计算机工作困难和行程硬件问题(如热运行和操作系统漏洞)在系统过度工作时发生。

270 进行了不同的测试。这包括练习浮动点、整数、位操作、控制流和虚拟内存测试的 CPU 特定测试。

注意

请谨慎使用 stress-ng 工具,因为某些测试可能会影响设计较差硬件上系统的热区域行点。这可能会影响系统性能,并导致过度出现系统延迟,这很难停止。

34.1. 测试 CPU 浮点单元和处理器数据缓存

浮点单元是处理器的功能部分,其执行浮点算术操作。浮点单元处理数学操作,使浮动数或十进制计算变得更加简单。

使用-- matrix-method 选项,您可以压力测试 CPU 浮动点操作和处理器数据缓存。

先决条件

  • 在系统中具有 root 权限

流程

  • 要测试一个 CPU 上的浮动点 60 秒,请使用 the- matrix 选项:

    # stress-ng --matrix 1 -t 1m
    Copy to Clipboard
  • 要在多个 CPU 上运行多个压力(60 秒),请使用- times or -t 选项:

    # stress-ng --matrix 0 -t 1m
    
    stress-ng --matrix 0 -t 1m --times
    stress-ng: info:  [16783] dispatching hogs: 4 matrix
    stress-ng: info:  [16783] successful run completed in 60.00s (1 min, 0.00 secs)
    stress-ng: info:  [16783] for a 60.00s run time:
    stress-ng: info:  [16783] 240.00s available CPU time
    stress-ng: info:  [16783] 205.21s user time   ( 85.50%)
    stress-ng: info:  [16783] 0.32s system time (  0.13%)
    stress-ng: info:  [16783] 205.53s total time  ( 85.64%)
    stress-ng: info:  [16783] load average: 3.20 1.25 1.40
    Copy to Clipboard

    具有 0 压力测试器的特殊模式,查询要运行的可用 CPU,无需指定 CPU 号。

    总 CPU 时间为 4 x 60 秒(240 秒),其中 0.13% 在内核中为 85.50%,用户时间为 85.64%,stress-ng 运行所有 CPU 的 85.64%。

  • 要使用 POSIX 消息队列测试进程间传递的消息,请使用 the -mq 选项:

    # stress-ng --mq 0 -t 30s --times --perf
    Copy to Clipboard

    mq 选项配置特定数量的进程,以使用 POSIX 消息队列强制上下文切换。这种压力测试旨在用于低数据缓存未命中。

34.2. 使用多个压力机制测试 CPU

stress-ng 工具运行多个压力测试。在默认模式中,它会并行运行指定的压力器机制。

先决条件

  • 在系统中具有 root 权限

流程

  • 运行多个 CPU 压力实例,如下所示:

    # stress-ng --cpu 2 --matrix 1 --mq 3 -t 5m
    Copy to Clipboard

    在示例中,stress-ng 运行 CPU 压力器的两个实例,其中一个矩阵压力者和三个消息队列压力测试的实例,以测试五分钟。

  • 要并行运行所有压力测试,请使用-- all 选项:

    # stress-ng --all 2
    Copy to Clipboard

    在本例中,stress-ng 会并行运行所有压力测试的两个实例。

  • 要在特定序列中运行每个不同的压力,请使用-- seq 选项。

    # stress-ng --seq 4 -t 20
    Copy to Clipboard

    在本例中,stress-ng 会逐一运行一次对 20 分钟的所有压力,每个压力或与在线 CPU 数量匹配的实例数量。

  • 要从测试运行中排除特定的压力,请使用 the -x 选项:

    # stress-ng --seq 1 -x numa,matrix,hdd
    Copy to Clipboard

    在本例中,stress-ng 运行所有压力器,每个实例都不包括 numa,hddkey ressors 机制。

34.3. 测量 CPU Heat 生成

为了测量 CPU 热代,指定的压力函数在短时间内生成高温度,以测试系统在最热生成之下的冷却可靠性和稳定性。使用-- matrix-size 选项,您可以在短时间内测量 Celsius 的 CPU 温度。

先决条件

  • 您在系统上具有 root 权限。

流程

  1. 要在指定持续时间的高温度中测试 CPU 行为,请运行以下命令:

    # stress-ng --matrix 0 --matrix-size 64 --tz -t 60
    
      stress-ng: info:  [18351] dispatching hogs: 4 matrix
      stress-ng: info:  [18351] successful run completed in 60.00s (1 min, 0.00 secs)
      stress-ng: info:  [18351] matrix:
      stress-ng: info:  [18351] x86_pkg_temp   88.00 °C
      stress-ng: info:  [18351] acpitz   87.00 °C
    Copy to Clipboard

    在本例中,stress-ng 将处理器软件包配置成rmal 区域,使其在 60 秒期间达到 88 位 Celsius。

  2. 可选: 要在运行结束时打印报告,请使用-- tz 选项:

    # stress-ng --cpu 0 --tz -t 60
    
      stress-ng: info:  [18065] dispatching hogs: 4 cpu
      stress-ng: info:  [18065] successful run completed in 60.07s (1 min, 0.07 secs)
      stress-ng: info:  [18065] cpu:
      stress-ng: info:  [18065] x86_pkg_temp   88.75 °C
      stress-ng: info:  [18065] acpitz   88.38 °C
    Copy to Clipboard

34.4. 使用 bogo 操作测量测试结果

stress-ng 工具可以通过测量每秒 bogo 操作来测量压力测试吞吐量。bogo 操作的大小取决于运行的压力。测试结果不是精确的,但提供了性能粗略估算。

您不能将此测量用作准确的基准指标。这些估计结果有助于了解不同内核版本或用于构建 stress-ng 的不同编译器版本中的系统性能变化。使用 --metrics-brief 选项显示可用 bogo 操作总数以及机器上的矩阵压力器性能。

先决条件

  • 您在系统上具有 root 权限。

流程

  • 要测量带有 bogo 操作的测试结果,请使用 with -metrics-brief 选项:

    # stress-ng --matrix 0 -t 60s --metrics-brief
    
    stress-ng: info: [17579] dispatching hogs: 4 matrix
    stress-ng: info: [17579] successful run completed in 60.01s (1 min, 0.01 secs)
    stress-ng: info: [17579] stressor bogo ops real time usr time sys time   bogo ops/s bogo ops/s
    stress-ng: info:  [17579]                  (secs)   (secs)  (secs)  (real time) (usr+sys time)
    stress-ng: info:  [17579] matrix  349322   60.00    203.23   0.19      5822.03      1717.25
    Copy to Clipboard

    --metrics-brief 选项显示测试结果,以及由 列表 压力者运行的总实时 bogo 操作(60 秒)。

34.5. 生成虚拟内存压力

在内存压力下,内核开始写出页面到交换。您可以使用 -page-in 选项强制非常数页面 交换回虚拟内存,对虚拟内存进行压力测试。这会导致虚拟机被大量练习。使用 --page-in 选项,您可以为 bigheapmmap 和虚拟机(vm)压力启用此模式。page-in 选项,touch 分配不在核心的页面,强制它们进入页面。

先决条件

  • 您在系统上具有 root 权限。

流程

  • 要压力测试虚拟内存,请使用 --page-in 选项:

    # stress-ng --vm 2 --vm-bytes 2G --mmap 2 --mmap-bytes 2G --page-in
    Copy to Clipboard

    在本例中,stress-ng 测试内存在有 4GB 内存的系统上(小于分配的缓冲区大小)、2 x 2GB 的 vm 压力和 2 x 2GB 的 mmap 压力(启用 页面)。

34.6. 测试设备上的大型中断负载

以高频率运行计时器可生成大型中断负载。带有适当所选计时器频率的-- timer 压力可能会强制每秒有多个中断。

先决条件

  • 您在系统上具有 root 权限。

流程

  • 要生成中断负载,请使用 the -timer 选项:

    # stress-ng --timer 32 --timer-freq 1000000
    Copy to Clipboard

    在本例中,stress-ng 测试 1MHz 的 32 个实例。

34.7. 在程序中生成主要页面错误

使用 stress-ng 时,您可以通过在内存中未加载的页面中生成主要页面错误来测试和分析页面错误率。在新内核版本中,用户faultfd 机制会通知错误查找进程虚拟内存布局中的页面错误。

先决条件

  • 您在系统上具有 root 权限。

流程

  • 要在早期内核版本中生成主要页面错误,请使用:

    # stress-ng --fault 0 --perf -t 1m
    Copy to Clipboard
  • 要在新内核版本中生成主要页面错误,请使用:

    # stress-ng --userfaultfd 0 --perf -t 1m
    Copy to Clipboard

34.8. 查看 CPU 压力测试机制

CPU 压力测试包含执行 CPU 的方法。您可以使用哪个选项打印输出来查看所有方法。

如果您没有指定测试方法,默认情况下,压力会以轮循方式检查所有压力,以测试每个压力者的 CPU。

先决条件

  • 您在系统上具有 root 权限。

流程

  1. 打印所有可用压力器机制,使用 哪个选项

    # stress-ng --cpu-method which
    
    cpu-method must be one of: all ackermann bitops callfunc cdouble cfloat clongdouble correlate crc16 decimal32 decimal64 decimal128 dither djb2a double euler explog fft fibonacci float fnv1a gamma gcd gray hamming hanoi hyperbolic idct int128 int64 int32
    Copy to Clipboard
  2. 使用-- cpu-method 选项指定特定的 CPU 压力方法:

    # stress-ng --cpu 1 --cpu-method fft -t 1m
    Copy to Clipboard

34.9. 使用验证模式

当测试处于活跃状态时,验证模式会验证结果。它将检查测试运行中的内存内容,并报告任何意外故障。

由于在这个模式下运行的额外验证步骤,所有压力者都没有验证模式,并且启用将减少 bogo 操作统计信息。

先决条件

  • 您在系统上具有 root 权限。

流程

  • 要验证压力测试结果,请使用-- verify 选项:

    # stress-ng --vm 1 --vm-bytes 2G --verify -v
    Copy to Clipboard

    在本例中,stress-ng 使用配置了 vm 压力模式,打印对虚拟映射的内存进行全面映射的内存检查的输出。它 sanity 检查内存的读取和写入结果。

第 35 章 创建并运行的容器

本节提供有关使用实时内核创建和运行容器的信息。

先决条件

  • 安装 podman 和其他容器相关工具。
  • 熟悉 RHEL 上 Linux 容器的管理和管理。
  • 安装 kernel-rt 软件包和其他实时相关软件包。

35.1. 创建容器

您可以将所有以下选项用于实时内核和主 RHEL 内核。kernel-rt 软件包带来了潜在的确定性改进,并允许常见的故障排除。

先决条件

  • 有管理员特权。

流程

以下流程描述了如何配置与实时内核相关的 Linux 容器。

  1. 创建您要用于容器的目录。例如:

    # mkdir cyclictest
    Copy to Clipboard
  2. 进入该目录:

    # cd cyclictest
    Copy to Clipboard
  3. 登录到提供容器 registry 服务的主机:

    # podman login registry.redhat.io
    Username: my_customer_portal_login
    Password: ***
    Login Succeeded!
    Copy to Clipboard
  4. 创建 Containerfile
  5. 如果您要从自定义 Containerfile 构建并构建 Containerfile,并构建它。以下是带有 cyclisttest 的示例:如果没有创建自己的镜像,您还可以拉取 realtime-tests-container 镜像来运行 cyclictest :

    # podman build -t cyclictest .
    Copy to Clipboard

35.2. 运行容器

您可以使用 Containerfile 运行构建的容器。

流程

  1. 使用 podman run 命令运行容器:

    # podman run --device=/dev/cpu_dma_latency --cap-add ipc_lock --cap-add sys_nice --cap-add sys_rawio --rm -ti cyclictest
    
    /dev/cpu_dma_latency set to 0us
    policy: fifo: loadavg: 0.08 0.10 0.09 2/947 15
    
    T: 0 ( 8) P:95 I:1000 C: 3209 Min: 1 Act: 1 Avg: 1 Max:  14
    
    T: 1 ( 9) P:95 I:1500 C: 2137 Min: 1 Act: 2 Avg: 1 Max:  23
    
    T: 2 (10) P:95 I:2000 C: 1601 Min: 1 Act: 2 Avg: 2 Max:   7
    
    T: 3 (11) P:95 I:2500 C: 1280 Min: 1 Act: 2 Avg: 2 Max:  72
    
    T: 4 (12) P:95 I:3000 C: 1066 Min: 1 Act: 1 Avg: 1 Max:   7
    
    T: 5 (13) P:95 I:3500 C:  913 Min: 1 Act: 2 Avg: 2 Max:  87
    
    T: 6 (14) P:95 I:4000 C:  798 Min: 1 Act: 1 Avg: 2 Max:   7
    
    T: 7 (15) P:95 I:4500 C:  709 Min: 1 Act: 2 Avg: 2 Max:  29
    Copy to Clipboard

本例演示了带有所需实时选项的 podman run 命令。例如:

  • 第一个 out (FIFO)调度程序策略通过 -cap-add=sys_nice 选项为容器中运行的工作负载提供。这个选项还允许在调整实时工作负载时设置线程的 CPU 关联性,另一个重要的配置维度。
  • --device=/dev/cpu_dma_latency 选项使主机设备在容器内可用(由 cyclictest 工作负载用来配置 CPU 空闲时间管理)。如果指定的设备不可用,则会出现类似以下消息的错误:

    WARN: stat /dev/cpu_dma_latency failed: No such file or directory

    当出现类似这些错误消息时,请参考 podman-run (1)手册页。要获得在容器内运行的特定工作负载,其他 podman-run 选项可能会很有用。

    在某些情况下,您还需要添加-- device=/dev/cpu 选项来添加 该目录层次结构,映射每个 CPU 设备文件,如 /dev/cpupassphrase/msr

第 36 章 显示进程的优先级

您可以使用 sched_getattr 属性显示进程优先级的信息,以及有关进程的调度策略的信息。

先决条件

  • 有管理员特权。

36.1. chrt 工具

chrt 工具检查并调整调度程序策略和优先级。它可以启动具有所需属性的新进程,或更改正在运行的进程的属性。

36.2. 使用 chrt 工具显示进程优先级

您可以显示指定进程的当前调度策略和调度优先级。

流程

  • 使用 a -p 选项运行 chrt 工具,并指定正在运行的进程。

    # chrt -p 468
    pid 468's current scheduling policy: SCHED_FIFO
    pid 468's current scheduling priority: 85
    
    # chrt -p 476
    pid 476's current scheduling policy: SCHED_OTHER
    pid 476's current scheduling priority: 0
    Copy to Clipboard

36.3. 使用 sched_getscheduler ()显示进程优先级

实时进程使用一组函数来控制策略和优先级。您可以使用 sched_getscheduler () 函数来显示指定进程的调度程序策略。

流程

  1. 创建 get_sched.c 源文件,并在文本编辑器中打开该文件。

    $ {EDITOR} get_sched.c
    Copy to Clipboard
  2. 将以下几行添加到 文件中。

    #include <sched.h>
    #include <unistd.h>
    #include <stdio.h>
    
    int main()
    {
      int policy;
      pid_t pid = getpid();
    
      policy = sched_getscheduler(pid);
      printf("Policy for pid %ld is %i.\n", (long) pid, policy);
      return 0;
    }
    Copy to Clipboard

    policy 变量保存指定进程的调度程序策略。

  3. 编译程序。

    $ gcc get_sched.c -o get_sched
    Copy to Clipboard
  4. 使用不同策略运行程序。

    $ chrt -o 0 ./get_sched
    Policy for pid 27240 is 0.
    $ chrt -r 10 ./get_sched
    Policy for pid 27243 is 2.
    $ chrt -f 10 ./get_sched
    Policy for pid 27245 is 1.
    Copy to Clipboard

36.4. 显示调度程序策略的有效范围

您可以使用 sched_get_priority_min ()sched_get_priority_max () 函数来检查给定调度程序策略的有效优先级范围。

流程

  1. 创建 sched_get.c 源文件,并在文本编辑器中打开该文件。

    $ {EDITOR} sched_get.c
    Copy to Clipboard
  2. 在文件中输入以下内容:

    #include <stdio.h>
    #include <unistd.h>
    #include <sched.h>
    
    int main()
    {
    
      printf("Valid priority range for SCHED_OTHER: %d - %d\n",
             sched_get_priority_min(SCHED_OTHER),
             sched_get_priority_max(SCHED_OTHER));
    
      printf("Valid priority range for SCHED_FIFO: %d - %d\n",
             sched_get_priority_min(SCHED_FIFO),
             sched_get_priority_max(SCHED_FIFO));
    
      printf("Valid priority range for SCHED_RR: %d - %d\n",
             sched_get_priority_min(SCHED_RR),
             sched_get_priority_max(SCHED_RR));
      return 0;
    }
    Copy to Clipboard
    注意

    如果系统不知道指定的调度程序策略,则函数 return -1errno 被设置为 EINVAL

    注意

    SCHED_FIFOSCHED_RR 可以是 199 范围内的任意数字。但是,POSIX 无法保证遵守此范围,而可移植的程序应使用这些功能。

  3. 保存文件并退出编辑器。
  4. 编译程序。

    $ gcc sched_get.c -o msched_get
    Copy to Clipboard

sched_get 程序现已就绪,可以从保存它的目录中运行。

36.5. 显示进程的时间片

SCHED_RR (round-robin)策略与 SCHED_FIFO (first-in、first-out)策略稍有不同。SCHED_RR 在循环轮转中分配具有相同优先级的并发进程。这样,每个进程都会被分配一个时间片。sched_rr_get_interval () 函数报告分配给每个进程的时间片。

注意

虽然 POSIX 要求这个功能只能用于配置为使用 SCHED_RR 调度程序策略运行的进程,但 sched_rr_get_interval () 函数可以检索 Linux 上任何进程的时间片长度。

timeslice 信息作为 timespec 返回。这是自 00:00:00 GMT 基础时间 1 月 1 日以来的秒数和纳秒:

struct timespec {
  time_t tv_sec;  /* seconds */
  long tv_nsec;   /* nanoseconds */
}
Copy to Clipboard

流程

  1. 创建 sched_timeslice.c 源文件,并在文本编辑器中打开该文件。

    $ {EDITOR} sched_timeslice.c
    Copy to Clipboard
  2. 将以下行添加到 sched_timeslice.c 文件中。

    #include <stdio.h>
    #include <sched.h>
    
    int main()
    {
       struct timespec ts;
       int ret;
    
       /* real apps must check return values */
       ret = sched_rr_get_interval(0, &ts);
    
       printf("Timeslice: %lu.%lu\n", ts.tv_sec, ts.tv_nsec);
    
       return 0;
    }
    Copy to Clipboard
  3. 保存文件并退出编辑器。
  4. 编译程序。

    $ gcc sched_timeslice.c -o sched_timeslice
    Copy to Clipboard
  5. 使用不同策略和优先级运行程序。

    $ chrt -o 0 ./sched_timeslice
    Timeslice: 0.38994072
    $ chrt -r 10 ./sched_timeslice
    Timeslice: 0.99984800
    $ chrt -f 10 ./sched_timeslice
    Timeslice: 0.0
    Copy to Clipboard

36.6. 显示进程的调度策略和相关属性

sched_getattr () 函数查询当前应用到指定进程的调度策略,由 PID 标识。如果 PID 等于零,则检索调用进程的策略。

size 参数应反映 sched_attr 结构的大小,如用户空间所知的。内核将 sched_attr::size 填充到其 sched_attr 结构的大小。

如果输入结构较小,内核会返回提供空间之外的值。因此,系统调用会失败,并显示 E2BIG 错误。其他 sched_attr 字段已填写,如 sched_attr 结构 中所述。

流程

  1. 创建 sched_timeslice.c 源文件,并在文本编辑器中打开该文件。

    $ {EDITOR} sched_timeslice.c
    Copy to Clipboard
  2. 将以下行添加到 sched_timeslice.c 文件中。

    #define _GNU_SOURCE
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <time.h>
    #include <linux/unistd.h>
    #include <linux/kernel.h>
    #include <linux/types.h>
    #include <sys/syscall.h>
    #include <pthread.h>
    
    #define gettid() syscall(__NR_gettid)
    
    #define SCHED_DEADLINE    6
    
    /* XXX use the proper syscall numbers */
    #ifdef __x86_64__
    #define __NR_sched_setattr        314
    #define __NR_sched_getattr        315
    #endif
    
    struct sched_attr {
         __u32 size;
         __u32 sched_policy;
         __u64 sched_flags;
    
         /* SCHED_NORMAL, SCHED_BATCH */
         __s32 sched_nice;
    
         /* SCHED_FIFO, SCHED_RR */
         __u32 sched_priority;
    
         /* SCHED_DEADLINE (nsec) */
         __u64 sched_runtime;
         __u64 sched_deadline;
         __u64 sched_period;
    };
    
    int sched_getattr(pid_t pid,
               struct sched_attr *attr,
               unsigned int size,
               unsigned int flags)
    {
         return syscall(__NR_sched_getattr, pid, attr, size, flags);
    }
    
    int main (int argc, char **argv)
    {
         struct sched_attr attr;
         unsigned int flags = 0;
         int ret;
    
         ret = sched_getattr(0, &attr, sizeof(attr), flags);
         if (ret < 0) {
             perror("sched_getattr");
             exit(-1);
         }
    
         printf("main thread pid=%ld\n", gettid());
         printf("main thread policy=%ld\n", attr.sched_policy);
         printf("main thread nice=%ld\n", attr.sched_nice);
         printf("main thread priority=%ld\n", attr.sched_priority);
         printf("main thread runtime=%ld\n", attr.sched_runtime);
         printf("main thread deadline=%ld\n", attr.sched_deadline);
         printf("main thread period=%ld\n", attr.sched_period);
    
         return 0;
    }
    Copy to Clipboard
  3. 编译 sched_timeslice.c 文件。

    $ gcc sched_timeslice.c -o sched_timeslice
    Copy to Clipboard
  4. 检查 sched_timeslice 程序的输出。

    $ ./sched_timeslice
    main thread pid=321716
    main thread policy=6
    main thread nice=0
    main thread priority=0
    main thread runtime=1000000
    main thread deadline=9000000
    main thread period=10000000
    Copy to Clipboard

36.7. sched_attr 结构

sched_attr 结构包含或定义指定线程的调度策略及其关联的属性。sched_attr 结构的格式如下:

struct sched_attr {
  u32 size;
  u32 sched_policy;
  u64 sched_flags;
  s32 sched_nice;
  u32 sched_priority;

  /* SCHED_DEADLINE fields */
  u64 sched_runtime;
  u64 sched_deadline;
  u64 sched_period;
};
Copy to Clipboard

sched_attr 数据结构

size

线程大小(以字节为单位)。如果结构的大小小于内核结构,则假定其他字段为 0。如果大小大于内核结构,内核会将所有其他字段验证为 0。

注意

sched_attr 结构大于内核结构大小时,sched_setattr () 函数会失败,并带有 E2BIG 错误。

sched_policy
调度策略
sched_flags

当进程使用 fork () 函数时,帮助控制调度行为。调用过程称为父进程,新进程称为子进程。有效值:

  • 0 :子进程从父进程继承调度策略。
  • SCHED_FLAG_RESET_ON_FORK: fork () :子进程不会从父进程继承调度策略。相反,它被设置为默认的调度策略 (struct sched_attr){ .sched_policy = SCHED_OTHER, }
sched_nice
指定在使用 SCHED_OTHERSCHED_BATCH 调度策略时设置的 nice 值。nice 值是范围为 -20 (高优先级)到 +19 (低优先级)的数字。
sched_priority
指定在调度 SCHED_FIFOSCHED_RR 时要设置的静态优先级。对于其他策略,将 priority 指定为 0。

只能为截止时间调度指定 SCHED_DEADLINE 字段:

  • sched_runtime :指定截止时间调度的 runtime 参数。该值以纳秒表示。
  • SCHED_ DEADLINE : 指定 截止时间 调度的截止时间参数。该值以纳秒表示。
  • sched_period :指定截止时间调度的 period 参数。该值以纳秒表示。

第 37 章 查看抢占状态

使用 CPU 的进程可以自愿或非自愿地放弃它们使用的 CPU。

37.1. 抢占

进程可以自愿地生成 CPU,因为它已经完成,或者由于它正在等待事件,如磁盘中的数据、键按下或网络数据包。

进程也可以不自愿地获得 CPU。这称为抢占,并在优先级较高的进程希望使用 CPU 时发生。

抢占可能会对系统性能造成负面影响,持续抢占可能会导致状态称为 thrashing。当进程被持续抢占且没有运行任何进程完成时,会出现这个问题。

更改任务的优先级有助于减少非自愿抢占。

37.2. 检查进程的抢占状态

您可以检查指定进程的 voluntary 和 involuntary 抢占状态。状态存储在 /proc/PID/status 中。

先决条件

  • 有管理员特权。

流程

  • 显示 /proc/PID/status 的内容,其中 PID 是进程的 ID。以下显示了 PID 为 1000 的进程的抢占状态。

    # grep voluntary /proc/1000/status
    voluntary_ctxt_switches: 194529
    nonvoluntary_ctxt_switches: 195338
    Copy to Clipboard

第 38 章 使用 chrt 工具为进程设置优先级

您可以使用 chrt 工具为进程设置优先级。

先决条件

  • 有管理员特权。

38.1. 使用 chrt 工具设置进程优先级

chrt 工具检查并调整调度程序策略和优先级。它可以启动具有所需属性的新进程,或更改正在运行的进程的属性。

流程

  • 要设置进程的调度策略,请使用适当的命令选项和参数运行 chrt 命令。在以下示例中,受命令影响的进程 ID 为 1000,优先级(-p)为 50

    # chrt -f -p 50 1000
    Copy to Clipboard

    要使用指定的调度策略和优先级启动应用程序,请根据需要添加应用程序名称和路径(如果需要)。

    # chrt -r -p 50 /bin/my-app
    Copy to Clipboard

    有关 chrt 工具选项的更多信息,请参阅 chrt 工具选项

38.2. chrt 工具选项

chrt 实用程序选项包括命令选项和参数,指定命令的进程和优先级。

策略选项

-f
将调度程序策略设置为 SCHED_FIFO
-o
将调度程序策略设置为 SCHED_OTHER
-r
将调度程序策略设置为 SCHED_RR (循环)。
-d
将调度程序策略设置为 SCHED_DEADLINE
-p n

将进程的优先级设置为 n

当将进程设置为 SCHED_DEADLINE 时,您必须指定 运行时截止时间周期 参数。

例如:

# chrt -d --sched-runtime 5000000 --sched-deadline 10000000 --sched-period 16666666 0 video_processing_tool
Copy to Clipboard

其中

  • --sched-runtime 5000000 是以纳秒为单位的运行时间。
  • --sched-deadline 10000000 是以纳秒为单位的相对截止时间。
  • --sched-period 16666666 是以纳秒为单位的时间段。
  • 0chrt 命令所需的未使用优先级的占位符。

第 39 章 为带有库调用的进程设置优先级

您可以使用 chrt 工具为进程设置优先级。

先决条件

  • 有管理员特权。

39.1. 用于设置优先级的库调用

实时进程使用一组不同的库调用来控制策略和优先级。以下库调用用于设置非实时进程的优先级。

  • nice
  • setpriority

这些功能调整非实时进程的 nice 值。nice 值充当调度程序的建议,建议将 ready-to-run、非实时进程列表的顺序在处理器上运行。列表头处的进程在进一步关闭列表之前运行。

重要

函数需要包含 sched.h 头文件。确保始终从功能检查返回代码。

39.2. 使用库调用设置进程优先级

可以使用 sched_setscheduler () 函数设置调度程序策略和其他参数。目前,实时策略有一个参数 sched_priority。这个参数用于调整进程的优先级。

sched_setscheduler () 函数需要三个参数,格式为: sched_setscheduler (pid_t pid, int policy, const struct sched_param *sp);

注意

sched_setscheduler (2) 手册页列出了 sched_setscheduler () 的所有可能返回值,包括错误代码。

如果进程 ID 为零,sched_setscheduler () 函数对调用进程执行操作。

以下代码摘录将当前进程的调度程序策略设置为 SCHED_FIFO 调度程序策略,并将优先级设置为 50

struct sched_param sp = { .sched_priority = 50 };
int ret;

ret = sched_setscheduler(0, SCHED_FIFO, &sp);
if (ret == -1) {
  perror("sched_setscheduler");
  return 1;
}
Copy to Clipboard

39.3. 使用库调用设置进程优先级参数

sched_setparam () 函数用于设置特定进程的调度参数。然后,可以使用 sched_getparam () 函数来验证这一点。

与仅返回调度策略的 sched_getscheduler () 函数不同,sched_getparam () 函数返回给定进程的所有调度参数。

流程

使用以下代码摘录来读取给定实时进程的优先级并递增它:

struct sched_param sp;
int ret;

ret = sched_getparam(0, &sp);
sp.sched_priority += 2;
ret = sched_setparam(0, &sp);
Copy to Clipboard

如果在真实应用程序中使用此代码,则需要从函数检查返回值并适当地处理任何错误。

重要

在递增优先级时请小心。如本例中,持续将两个添加到调度程序优先级时,可能会导致无效的优先级。

39.4. 为进程设置调度策略和相关属性

sched_setattr () 函数为 PID 中指定的实例 ID 设置调度策略及其关联的属性。当 pid=0 时,sched_setattr () 会对调用线程的进程和属性执行操作。

流程

  • 调用 sched_setattr () 指定调用操作的进程 ID,以及以下实时调度策略之一:

实时调度策略

SCHED_FIFO
调度第一内和第一出策略。
SCHED_RR
调度循环策略。
SCHED_DEADLINE
调度截止时间调度策略。

Linux 还支持以下非实时调度策略:

非实时调度策略

SCHED_OTHER
调度标准循环时间共享策略。
SCHED_BATCH
调度进程的"批量"风格执行。
SCHED_IDLE

调度非常低的优先级的后台作业。SCHED_IDLE 只能以静态优先级 0 使用,而 nice 值对此策略没有影响。

此策略旨在以非常低的优先级运行作业(低于使用 SCHED_OTHERSCHED_BATCH 策略的 +19 nice 值)。

第 40 章 在实时内核和解决方案中调度问题

实时内核中的调度有时可能会导致。通过使用提供的信息,您可以了解调度策略、调度程序节流和线程不足状态的问题,以及潜在的解决方案。

40.1. 实时内核的调度策略

实时调度策略共享一个主要特征:它们运行直到高优先级线程通过休眠或执行 I/O 中断线程或线程等待。

对于 SCHED_RR,操作系统会中断一个正在运行的线程,以便相同 SCHED_RR 优先级的另一个线程可以运行。在这两种情况下,POSIX 规格不会进行置备,该规范定义了允许较低优先级线程获得任何 CPU 时间的策略。实时线程的这种特性意味着,可轻松编写应用程序,这会对给定 CPU 的 100% 进行单调。但是,这会导致操作系统出现问题。例如,操作系统负责管理系统范围和每个 CPU 资源,必须定期检查描述这些资源的数据结构,并使用它们执行内务操作。但是,如果内核被 SCHED_FIFO 线程 monopolized,则无法执行其内务任务。最终,整个系统变得不稳定,并可能导致崩溃。

在 RHEL for Real Time 内核中,中断处理程序作为具有 SCHED_FIFO 优先级的线程运行。默认优先级为 50。高于中断处理器线程的 SCHED_FIFOSCHED_RR 策略的 cpu-hog 线程可能会阻止中断处理程序运行。这会导致程序等待这些中断信号的数据,并失败。

40.2. 实时内核中的调度程序节流

实时内核包含一个保护机制,用于启用分配给实时任务使用的带宽。保护机制称为实时调度程序节流。

实时节流机制的默认值定义了实时任务可以使用 95% 的 CPU 时间。剩余的 5% 将专用于非实时任务,例如在 SCHED_OTHER 和类似调度策略下运行的任务。务必要注意,如果单个实时任务占用 95% 的 CPU 时间插槽,则该 CPU 上的剩余实时任务将不会运行。只有非实时任务使用剩余的 CPU 时间。默认值可以有以下性能影响:

  • 实时任务最多有 95% 的 CPU 时间,这可能会影响其性能。
  • 实时任务不允许运行非实时任务来锁定系统。

实时调度程序节流由 /proc 文件中的以下参数控制:

/proc/sys/kernel/sched_rt_period_us 参数
定义在 CPU 带宽的 100% 中的周期(微秒)。默认值为 1,000,000 HEKETIs,即 1 秒。必须仔细考虑对 period 值的更改,因为一个期限值非常高或低可能会导致问题。
/proc/sys/kernel/sched_rt_runtime_us 参数
定义可用于所有实时任务的总带宽。默认值为 950,000 HEKETIs (0.95 s),这是 CPU 带宽的 95%。将值设为 -1 将配置实时任务,使其最多使用 100% 的 CPU 时间。只有当实时任务经过精心设计且没有明显的注意事项(如未绑定的轮询循环)时,这才有效。

40.3. 实时内核中的线程不足

当线程位于 CPU 运行队列中超过星级阈值且没有进行进度时,线程不足发生。线程不足的常见原因是运行固定优先级轮询应用程序,如 SCHED_FIFO 或与 CPU 绑定的 SCHED_RR。由于轮询应用程序对于 I/O 并不阻止,因此这可以防止其他线程(如 kworkers )无法在该 CPU 上运行。

早期尝试减少线程不足情况被称为实时节流。在实时节流中,每个 CPU 都有一部分执行时间专用于非实时任务。节流的默认设置为使用 95% 的 CPU,为非实时任务保留 5%。如果您有一个单一实时任务导致了不足,但如果为 CPU 分配多个实时任务,则无法正常工作。您可以使用以下方法解决这个问题:

stalld 机制

stalld 机制是实时节流的替代选择,并避免了一些节流的缺陷。stalld 是一个守护进程,用于定期监控系统中每个线程的状态,并查找运行队列中的线程,以便在不运行的情况下运行。stalld 临时更改该线程以使用 SCHED_DEADLINE 策略,并在指定的 CPU 上分配少量时间。然后,线程会运行,在使用时间片段时,线程会返回其原始调度策略,而 stalld 继续监控线程状态。

日常 CPU 是运行所有守护进程、shell 进程、内核线程、中断处理程序以及可以从隔离的 CPU 分配的所有工作的 CPU。对于禁用实时节流的内务 CPU,stopd 会监控运行主工作负载的 CPU,并为使用 SCHED_FIFO 忙碌循环分配 CPU,这有助于检测停止的线程并根据需要改进之前定义的可接受的 noise 的线程优先级。如果实时节流机制导致主工作负载出现不合理的通知,则 stalld 可能会是一个首选。

使用 stalld 时,您可以通过提高不足的线程来更精确地控制引入的 noise。shell 脚本 /usr/bin/throttlectl 会在运行 stalld 时自动禁用实时节流。您可以使用 /usr/bin/throttlectl show 脚本列出当前的节流值。

禁用实时节流

/proc 文件系统中的以下参数控制实时节流:

  • /proc/sys/kernel/sched_rt_period_us 参数指定一个句点中的微秒数,默认为 100万,即 1 秒。
  • /proc/sys/kernel/sched_rt_runtime_us 参数指定在节流发生前实时任务使用的微秒数,并默认为可用 CPU 周期的 950,000 或 95%。您可以使用 echo -1 > /proc/sys/kernel/ sched_rt_runtime_us 命令,将值-1 传递给 sched_rt_runtime_us 文件来禁用节流。

法律通告

Copyright © 2025 Red Hat, Inc.
The text of and illustrations in this document are licensed by Red Hat under a Creative Commons Attribution–Share Alike 3.0 Unported license ("CC-BY-SA"). An explanation of CC-BY-SA is available at http://creativecommons.org/licenses/by-sa/3.0/. In accordance with CC-BY-SA, if you distribute this document or an adaptation of it, you must provide the URL for the original version.
Red Hat, as the licensor of this document, waives the right to enforce, and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent permitted by applicable law.
Red Hat, Red Hat Enterprise Linux, the Shadowman logo, the Red Hat logo, JBoss, OpenShift, Fedora, the Infinity logo, and RHCE are trademarks of Red Hat, Inc., registered in the United States and other countries.
Linux® is the registered trademark of Linus Torvalds in the United States and other countries.
Java® is a registered trademark of Oracle and/or its affiliates.
XFS® is a trademark of Silicon Graphics International Corp. or its subsidiaries in the United States and/or other countries.
MySQL® is a registered trademark of MySQL AB in the United States, the European Union and other countries.
Node.js® is an official trademark of Joyent. Red Hat is not formally related to or endorsed by the official Joyent Node.js open source or commercial project.
The OpenStack® Word Mark and OpenStack logo are either registered trademarks/service marks or trademarks/service marks of the OpenStack Foundation, in the United States and other countries and are used with the OpenStack Foundation's permission. We are not affiliated with, endorsed or sponsored by the OpenStack Foundation, or the OpenStack community.
All other trademarks are the property of their respective owners.
返回顶部
Red Hat logoGithubredditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

通过我们的产品和服务,以及可以信赖的内容,帮助红帽用户创新并实现他们的目标。 了解我们当前的更新.

让开源更具包容性

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。欲了解更多详情,请参阅红帽博客.

關於紅帽

我们提供强化的解决方案,使企业能够更轻松地跨平台和环境(从核心数据中心到网络边缘)工作。

Theme

© 2025 Red Hat