了解 RHEL for Real Time
了解 RHEL for Real Time 内核的主要概念和使用
摘要
对红帽文档提供反馈
我们感谢您对我们文档的反馈。让我们了解如何改进它。
通过 Jira 提交反馈(需要帐户)
- 登录到 Jira 网站。
- 在顶部导航栏中点 Create
- 在 Summary 字段中输入描述性标题。
- 在 Description 字段中输入您对改进的建议。包括文档相关部分的链接。
- 点对话框底部的 Create。
第 1 章 RHEL for Real Time 的硬件平台
在设置实时环境时,配置硬件可以正确地发挥关键作用,因为硬件会影响到您的系统运行的方式。并非所有硬件平台都能够实时完成,并且启用调优。在进行微调前,您必须确保潜在的硬件平台实时功能。
硬件平台因供应商而异。您可以使用硬件延迟检测器(hwlatdetect
)程序实时测试并验证硬件是否适合性。程序控制延迟检测器内核模块,并帮助检测底层硬件或固件行为导致的延迟。
完成低延迟操作所需的所有调整步骤。有关降低低延迟问题并改进调整的说明,请参阅厂商文档。
先决条件
- 已安装 RHEL-RT 软件包。
完成低延迟操作所需的所有调整步骤。有关减少或删除使系统移至系统管理模式(SMM)的系统管理中断(SMI)的说明,请参阅厂商文档。
警告您必须完全避免禁用系统管理中断(SMI),因为它可能会导致严重硬件故障。
1.1. 处理器内核
实时处理器核心是物理中央处理单元(CPU),它执行机器代码。套接字是处理器和计算机主板之间的连接。套接字是处理器放入的主板上的位置。有两组处理器:
- 单一核心处理器占用一个可用内核。
- 四核处理器会占用有四个可用内核的一个插槽。
在设计实时环境时,请注意可用内核的数量、内核之间的缓存布局以及内核物理连接方式。
当多个内核可用时,请使用线程或进程。在不使用这些构造的情况下编写的程序,可以一次在单个处理器中运行。多核平台通过对不同类型的操作使用不同的内核来提供优势。
缓存
缓存对整个处理时间和确定性有显著影响。通常,应用的线程需要同步对共享资源(如数据结构)的访问。
使用 tuna
命令行工具(CLI),您可以确定缓存布局并将交互线程绑定到内核,以便它们共享缓存。缓存共享通过确保相互排除原语(mutex、条件变量或类似结构)和数据结构使用相同的缓存来减少内存错误。
互连
增加系统上的内核数可能会导致互连的冲突需求。这样,需要确定互连拓扑,以帮助检测实时系统上内核之间出现的冲突。
现在,许多硬件供应商在内核和内存之间提供透明的互连网络,称为非统一内存访问(NUMA)架构。
NUMA 是多处理中使用的系统内存设计,其中内存访问时间取决于处理器的内存位置。使用 NUMA 时,处理器可以访问自己的本地内存比非本地内存快,如处理器之间共享的其他处理器或内存上的内存。在 NUMA 系统中,了解互连拓扑有助于放置经常在相邻内核上通信的线程。
taskset
和 numactl
工具决定了 CPU 拓扑。taskset
定义没有 NUMA 资源(如内存节点和 numactl
)的 CPU 关联性控制进程和共享内存的 NUMA 策略。
第 2 章 RHEL for Real Time 上的内存管理
实时系统使用虚拟内存系统,其中由用户空间应用引用的地址转换为物理地址。转换通过底层计算系统中的页表和地址转换硬件的组合进行。在程序与实际内存之间具有转换机制的优点是操作系统在需要时或 CPU 请求时可以交换页面。
在实时,若要从存储交换页面到主内存,之前使用的页表条目被标记为无效。因此,即使出现正常内存压力,操作系统也可以从一个应用程序检索页面,并将其提供给另一个应用程序。这可能导致无法预计的系统行为。
内存分配实现包括需求分页机制和内存锁定(mlock
())系统调用。
在不同缓存和 NUMA 域中共享 CPU 的数据信息可能会导致流量问题和瓶颈。
在编写多线程应用程序时,在设计数据分类前考虑机器拓扑。Topology 是内存层次结构,包括 CPU 缓存和 Non-Uniform Memory Access (NUMA)节点。
2.1. 需求分页
需求分页与带有页面交换的分页系统类似。系统在需要或 CPU 需求时加载存储在辅助内存中的页面。程序生成的所有内存地址都通过处理器中的地址转换机制。然后,地址从特定于进程的虚拟地址转换为物理内存地址。这称为虚拟内存。转换机制中的两个主要组件是页表,翻译的缓冲(TLB)
页面表
页面表是物理内存中的多级表,其中包含虚拟到物理内存的映射。这些映射可以被处理器中的虚拟内存转换硬件读取。
含有分配物理地址的页表条目称为常驻工作集。当操作系统需要为其他进程释放内存时,它可以从常驻工作集中交换页面。交换页面时,对该页面中的虚拟地址的任何引用都会创建页面错误,并导致页面重新分配。
当系统物理内存非常低时,交换过程将开始 thrash (持续从进程窃取页面),且永远不会允许某个进程完成。您可以通过在 /proc/vmstat
文件中查找 pgfault
值来监控虚拟内存统计信息。
翻译查找缓冲区
translation Lookaside Buffers (TLB)是虚拟内存转换的硬件缓存。任何带有 TLB 的处理器核心在启动页表条目的内存读取时并行检查 TLB。如果虚拟地址的 TLB 条目有效,则内存读取将中止,并且 TLB 中的值用于地址转换。
TLB 根据引用的本地原则进行操作。这意味着,如果代码在一段时间内处于一个内存中(如循环或调用相关的功能),则 TLB 引用避免了地址转换的主要内存。这可显著加快处理时间。
在编写确定性和快速代码时,请使用保持参考本地功能的功能。这可能意味着使用循环而不是递归。如果递归不可避免,请将递归调用放在函数的末尾。这称为 tail-recursion,这使得代码在相对较小的内存中工作,并避免从主内存调用表转换。
2.2. 主要和次要页面错误
RHEL for Real Time 通过将物理内存拆分为称为页面的块来分配内存,然后将它们映射到虚拟内存。当进程需要没有映射或内存中不再提供的特定页面时,会实时发生故障。因此,故障实际上意味着 CPU 要求时页面不可用。当进程遇到页面错误时,所有线程都会冻结,直到内核处理这个错误为止。解决此问题的方法有多种,但最佳解决方案可以调整源代码以避免页面错误。
次要页面错误
当进程在初始化前试图访问一部分内存时,实时会出现次要页面错误。在这种情况下,系统会执行操作来填充内存映射或其他管理结构。次要页面错误的严重性取决于系统负载和其他因素,但它们通常很短,并会影响到不计。
主页错误
当系统必须与属于其他进程的磁盘、交换内存页面或执行任何其他输入输出(I/O)活动来释放内存时,会发生实时重大错误。当处理器引用没有为其分配物理页面的虚拟内存地址时会出现这种情况。对空页面的引用会导致处理器执行错误,并指示内核代码分配页面,这会显著增加延迟。
在实时,当应用程序显示性能丢弃时,检查 /proc/
目录中页面错误的进程信息会很有帮助。对于使用 cat
命令的特定进程标识符(PID),您可以查看以下相关条目的 /proc/PID/stat
文件:
- 字段 2:可执行文件文件名。
- 字段 10:次要页面错误的数量。
- 字段 12:主页面错误的数量。
以下示例演示了使用 cat
命令查看页面错误并使用 pipe
功能,只返回 /proc/PID/stat
文件的第二行、第十和第十二行:
cat /proc/3366/stat | cut -d\ -f2,10,12 (bash) 5389 0
# cat /proc/3366/stat | cut -d\ -f2,10,12
(bash) 5389 0
在示例输出中,PID 3366 的进程是 bash,它有 5389 次要页面错误,且没有主要页面错误。
2.3. mlock 系统调用
内存锁定(mlock ()
)系统调用允许调用进程锁定或解锁指定的地址空间范围,并防止 Linux 分页锁定的内存以交换空间。将物理页面分配给页表条目后,对该页的引用相对快。内存锁定系统调用属于 mlock ()
和 munlock ()
类别。
mlock ()
和 munlock ()
系统调用锁定并解锁指定的进程地址页面范围。成功时,指定范围内的页面将保留在内存中,直到 munlock ()
系统调用解锁页面。
mlock ()
和 munlock ()
系统调用采用以下参数:
-
addr
: 指定地址范围的开头。 -
len
:指定地址空间的长度,以字节为单位。
成功时,mlock ()
和 munlock ()
系统调用返回 0。如果出现错误,则返回 -1 并设置 errno
来指示错误。
mlockall ()
和 munlockall ()
系统调用锁定或解锁所有程序空间。
mlock ()
系统调用不能确保程序没有页面 I/O。它确保数据保留在内存中,但不能确保它保留在同一页面上。无论使用 mlock ()
,其他功能(如 move_pages
和 memory compactors)都可以移动数据。
内存锁定是以页为基础进行的,不堆栈。如果两个动态分配的内存片段通过 mlock ()
或 mlockall ()
共享同一页面,它们通过使用单个 munlock ()
或 munlockall ()
系统调用来解锁。因此,务必要了解应用程序解锁的页面,以避免出现双锁或单解锁问题。
以下是缓解双锁或单解锁问题的最常见的临时解决方案:
- 跟踪已分配和锁定的内存区域,并创建打包程序功能,在解锁页面前验证页面分配数量。这是设备驱动程序中使用的资源计数原则。
- 根据页大小和校准进行内存分配,以避免在页面上出现双锁定。
第 3 章 使用压力测试实时系统
stress-ng
工具测量系统的能力,以便在不可尝试的情况下保持良好的效率水平。stress-ng
工具是一种压力工作负载生成器,用于加载和压力所有内核接口。它包括广泛的压力机制,称为压力者。压力测试使得计算机工作困难和行程硬件问题(如热运行和操作系统漏洞)在系统过度工作时发生。
270 进行了不同的测试。这包括练习浮动点、整数、位操作、控制流和虚拟内存测试的 CPU 特定测试。
请谨慎使用 stress-ng
工具,因为某些测试可能会影响设计较差硬件上系统的热区域行点。这可能会影响系统性能,并导致过度出现系统延迟,这很难停止。
3.1. 测试 CPU 浮点单元和处理器数据缓存
浮点单元是处理器的功能部分,其执行浮点算术操作。浮点单元处理数学操作,使浮动数或十进制计算变得更加简单。
使用-- matrix-method
选项,您可以压力测试 CPU 浮动点操作和处理器数据缓存。
先决条件
- 在系统中具有 root 权限
流程
要测试一个 CPU 上的浮动点 60 秒,请使用 the-
matrix
选项:stress-ng --matrix 1 -t 1m
# stress-ng --matrix 1 -t 1m
Copy to Clipboard Copied! 要在多个 CPU 上运行多个压力(60 秒),请使用-
times or
-t
选项:stress-ng --matrix 0 -t 1m
# 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 Copied! 具有 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
# stress-ng --mq 0 -t 30s --times --perf
Copy to Clipboard Copied! mq
选项配置特定数量的进程,以使用 POSIX 消息队列强制上下文切换。这种压力测试旨在用于低数据缓存未命中。
3.2. 使用多个压力机制测试 CPU
stress-ng
工具运行多个压力测试。在默认模式中,它会并行运行指定的压力器机制。
先决条件
- 在系统中具有 root 权限
流程
运行多个 CPU 压力实例,如下所示:
stress-ng --cpu 2 --matrix 1 --mq 3 -t 5m
# stress-ng --cpu 2 --matrix 1 --mq 3 -t 5m
Copy to Clipboard Copied! 在示例中,
stress-ng
运行 CPU 压力器的两个实例,其中一个矩阵压力者和三个消息队列压力测试的实例,以测试五分钟。要并行运行所有压力测试,请使用--
all
选项:stress-ng --all 2
# stress-ng --all 2
Copy to Clipboard Copied! 在本例中,
stress-ng
会并行运行所有压力测试的两个实例。要在特定序列中运行每个不同的压力,请使用--
seq
选项。stress-ng --seq 4 -t 20
# stress-ng --seq 4 -t 20
Copy to Clipboard Copied! 在本例中,
stress-ng
会逐一运行一次对 20 分钟的所有压力,每个压力或与在线 CPU 数量匹配的实例数量。要从测试运行中排除特定的压力,请使用 the
-x
选项:stress-ng --seq 1 -x numa,matrix,hdd
# stress-ng --seq 1 -x numa,matrix,hdd
Copy to Clipboard Copied! 在本例中,
stress-ng
运行所有压力器,每个实例都不包括numa
,hdd
和key
ressors 机制。
3.3. 测量 CPU Heat 生成
为了测量 CPU 热代,指定的压力函数在短时间内生成高温度,以测试系统在最热生成之下的冷却可靠性和稳定性。使用-- matrix-size
选项,您可以在短时间内测量 Celsius 的 CPU 温度。
先决条件
- 您在系统上具有 root 权限。
流程
要在指定持续时间的高温度中测试 CPU 行为,请运行以下命令:
stress-ng --matrix 0 --matrix-size 64 --tz -t 60
# 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 Copied! 在本例中,
stress-ng
将处理器软件包配置成rmal 区域,使其在 60 秒期间达到 88 位 Celsius。可选: 要在运行结束时打印报告,请使用--
tz
选项:stress-ng --cpu 0 --tz -t 60
# 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 Copied!
3.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 --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 Copied! --metrics-brief
选项显示测试结果,以及由列表
压力者运行的总实时 bogo 操作(60 秒)。
3.5. 生成虚拟内存压力
在内存压力下,内核开始写出页面到交换。您可以使用 -page-in 选项强制非常数页面
交换回虚拟内存,对虚拟内存进行压力测试。这会导致虚拟机被大量练习。使用 --page-in
选项,您可以为 bigheap
、mmap
和虚拟机(vm
)压力启用此模式。page-in
选项,touch 分配不在核心的页面,强制它们进入页面。
先决条件
- 您在系统上具有 root 权限。
流程
要压力测试虚拟内存,请使用
--page-in
选项:stress-ng --vm 2 --vm-bytes 2G --mmap 2 --mmap-bytes 2G --page-in
# stress-ng --vm 2 --vm-bytes 2G --mmap 2 --mmap-bytes 2G --page-in
Copy to Clipboard Copied! 在本例中,
stress-ng
测试内存在有 4GB 内存的系统上(小于分配的缓冲区大小)、2 x 2GB 的vm
压力和 2 x 2GB 的mmap
压力(启用页面)。
3.6. 测试设备上的大型中断负载
以高频率运行计时器可生成大型中断负载。带有适当所选计时器频率的-- timer
压力可能会强制每秒有多个中断。
先决条件
- 您在系统上具有 root 权限。
流程
要生成中断负载,请使用 the
-timer
选项:stress-ng --timer 32 --timer-freq 1000000
# stress-ng --timer 32 --timer-freq 1000000
Copy to Clipboard Copied! 在本例中,
stress-ng
测试 1MHz 的 32 个实例。
3.7. 在程序中生成主要页面错误
使用 stress-ng
时,您可以通过在内存中未加载的页面中生成主要页面错误来测试和分析页面错误率。在新内核版本中,用户faultfd
机制会通知错误查找进程虚拟内存布局中的页面错误。
先决条件
- 您在系统上具有 root 权限。
流程
要在早期内核版本中生成主要页面错误,请使用:
stress-ng --fault 0 --perf -t 1m
# stress-ng --fault 0 --perf -t 1m
Copy to Clipboard Copied! 要在新内核版本中生成主要页面错误,请使用:
stress-ng --userfaultfd 0 --perf -t 1m
# stress-ng --userfaultfd 0 --perf -t 1m
Copy to Clipboard Copied!
3.8. 查看 CPU 压力测试机制
CPU 压力测试包含执行 CPU 的方法。您可以使用哪个选项打印输出来查看所有方法。
如果您没有指定测试方法,默认情况下,压力会以轮循方式检查所有压力,以测试每个压力者的 CPU。
先决条件
- 您在系统上具有 root 权限。
流程
打印所有可用压力器机制,使用
哪个选项
:stress-ng --cpu-method which
# 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 Copied! 使用--
cpu-method
选项指定特定的 CPU 压力方法:stress-ng --cpu 1 --cpu-method fft -t 1m
# stress-ng --cpu 1 --cpu-method fft -t 1m
Copy to Clipboard Copied!
3.9. 使用验证模式
当测试处于活跃状态时,验证模式会验证结果。它将检查测试运行中的内存内容,并报告任何意外故障。
由于在这个模式下运行的额外验证步骤,所有压力者都没有验证模式,并且启用将减少 bogo 操作统计信息。
先决条件
- 您在系统上具有 root 权限。
流程
要验证压力测试结果,请使用--
verify
选项:stress-ng --vm 1 --vm-bytes 2G --verify -v
# stress-ng --vm 1 --vm-bytes 2G --verify -v
Copy to Clipboard Copied! 在本例中,
stress-ng
使用配置了vm
压力模式,打印对虚拟映射的内存进行全面映射的内存检查的输出。它 sanity 检查内存的读取和写入结果。
第 4 章 RHEL for Real Time 上的硬件中断
实时系统在其操作过程中收到许多中断,包括定期执行维护和系统调度决策的半常规"timer"中断。系统也可以接收特殊类型的中断,如不可屏蔽中断(NMI)和系统管理中断(SMI)。设备使用硬件中断来指示需要注意的系统的物理状态的更改。例如,一个硬盘表示它已读取一系列数据块,或者网络设备处理了包含网络数据包的缓冲区时。
当中断实时发生时,系统会停止活跃的程序,并执行中断处理程序。
在实时中,硬件中断由中断号引用。这些数字映射到创建中断的硬件片段。这可让系统监控哪个设备创建了中断以及发生的时间。当中断实时发生时,系统会停止活跃的程序并执行中断处理程序。处理程序会抢占其他运行的程序和系统活动。这会减慢整个系统并创建延迟。
RHEL for Real Time 修改处理中断的方式,以提高性能并降低延迟。使用 cat /proc/interrupts
命令,您可以打印输出来查看所发生的硬件中断类型、接收中断数、中断的目标 CPU 以及生成中断的设备。
4.1. 级别的信号中断
在实时,level-signaled 中断使用提供 voltage 转换的专用中断行。设备控制器通过在中断请求行中指示信号来引发中断。interrupt 行发送两个 voltages 中的一个来代表二进制 1 或二进制 0。
当中断信号由 行发送时,它会保持该状态,直到 CPU 重置为止。CPU 执行状态保存、捕获中断并分配中断处理程序。中断处理程序决定了中断的原因,通过执行必要的服务清除中断,然后恢复设备的状态。级别信号的中断更为可靠,但支持多个设备,尽管它们比较复杂。
4.2. 消息信号中断
在实时中,许多系统使用消息信号中断(MSI),它会在数据包或基于消息的电总线上发送信号作为专用消息。这种总线的常见示例是 Peripheral Component Interconnect Express (PCI Express 或 PCIe)。这些设备传输消息类型,PCIe 主机控制器将解释为中断消息。然后,主机控制器将 上的消息发送到 CPU。
根据硬件实时,PCIe 系统执行以下操作之一:
- 使用 PCIe 主机控制器和 CPU 之间的专用中断行发送信号。
- 通过 CPU HyperTransport 总线发送消息。
在实时,PCIe 系统也可以以旧模式运行,实施旧的中断行以支持旧的操作系统,或使用内核命令行上的 pci=nomsi 选项在引导 Linux 内核中使用选项 pci=nomsi
。
4.3. 不可屏蔽中断
在实时,不可屏蔽的中断是系统中标准中断屏蔽技术的硬件中断。NMI 的优先级高于可屏蔽中断。发生 NMI 来表示不可恢复的硬件错误。
在实时中,NMIM 也被某些系统用作硬件监控器。当处理器收到 NMI 时,它通过调用 interrupt vector 指向的 NMI 处理程序来立即处理 NMI。如果满足某些条件,如在指定时间后不会触发中断,NMI 处理程序会发出警告,并提供有关此问题的调试信息。这有助于识别和防止系统锁定。
在实时,可屏蔽中断是通过在中断掩码寄存器的位掩码中设置位来忽略的硬件中断。CPU 可以在关键处理过程中临时忽略可屏蔽的中断。
4.4. 系统管理中断
在实时中,系统管理中断(SMI)提供扩展功能,如传统硬件设备仿真,也可用于系统管理任务。SMI 与不可屏蔽中断(NMI)类似,它们使用特殊的电缆信号行,通常不可屏蔽。当发生 SMI 时,CPU 会进入系统管理模式(SMM)。在这个模式中,执行一个特殊的低级别处理程序来处理 SMI。SMM 通常直接从系统管理固件提供,通常是 BIOS 或 EFI。
实时 SMI 最常用于提供传统的硬件模拟。一个常见示例是模拟磁盘驱动器。如果没有附加磁盘驱动器,操作系统会尝试访问软盘并触发 SMI。在这种情况下,处理程序为操作系统提供模拟设备。然后,操作系统会将模拟视为旧设备。
实时,SMI 可能会对系统造成负面影响,因为它们不需要直接参与操作系统。编写较差的 SMI 处理例程可能会消耗很多毫秒的 CPU 时间,操作系统可能无法抢占处理器。这会在其他精心调优和高度响应的系统中创建定期高延迟。作为供应商可能使用 SMI 处理程序来管理 CPU 温度和有机控制,因此可能无法禁用它们。在这种情况下,您必须通知厂商在使用这些中断时发生的问题。
实时,您可以使用 hwlatdetect
工具隔离 SMI。它包括在 rt-tests
软件包中。这个工具测量 SMI 处理例程使用 CPU 的时间周期。
4.5. 高级可编程中断控制器
Intel 公司开发的高级可编程中断控制器(APIC)提供了以下功能:
- 处理大量中断,将每个中断路由到特定的 CPU 集合。
- 支持 CPU 间的通信,并不再需要多个设备共享单个中断行。
实时 APIC 代表一系列设备和技术,它们一起以可扩展且可管理的方式生成、路由和处理大量硬件中断。它使用内置于每个系统 CPU 中的本地 APIC 和多个输入/输出 APIC 的组合,它们直接连接到硬件设备。
在实时中,当硬件设备生成中断时,连接的 I/O APIC 会检测并将系统 APIC 总线上的中断路由到特定的 CPU。操作系统知道 IO-APIC 连接到设备,并在该设备中中断行。高级配置和电源接口不同的系统描述表(ACPI DSDT)包含有关主机系统主板和外设组件的具体信息,设备提供有关可用中断源的信息。这两个数据集一起提供有关总体中断层次结构的信息。
RHEL for Real Time 支持基于 Complex APIC 的中断管理策略,并在层次结构中连接系统 APIC,并以负载均衡方式向 CPU 提供中断,而不是以特定的 CPU 或一组 CPU 为目标。
第 5 章 用于实时进程和线程的 RHEL
RHEL for Real Time 键的因素是最小的中断延迟和最小线程切换延迟。虽然所有程序都使用线程和流程,但 RHEL for Real Time 与标准 Red Hat Enterprise Linux 相比以不同的方式处理它们。
在实时使用 parallelism 时,有助于提高任务执行和延迟的效率。parallelism 是在使用 CPU 的多核基础架构同时运行多个任务或多个子任务时。
5.1. Process
在最简单的术语中,实时进程是执行的一个程序。术语 进程指的是独立的地址空间,可能包含多个线程。当开发了一个地址空间内运行的进程的概念时,Linux 转而成一种进程结构,与另一个进程共享地址空间。只要进程数据结构很小,这就可以正常工作。
UNIX® 风格的进程结构包括:
- 虚拟内存的地址映射.
- 执行上下文(PC、堆栈、寄存器)。
- 状态和核算信息。
在实时中,每个进程都以一个线程开头,通常称为父线程。您可以使用 fork ()
系统调用从父线程创建额外的线程。fork ()
创建一个新的子进程,它与父进程相同,但新进程标识符除外。子进程独立于创建进程运行。父进程和子进程可以同时执行。fork ()
和 exec ()
系统调用的区别在于,fork ()
会启动一个新的进程,即父进程的副本,exec ()
将当前进程镜像替换为新的进程镜像。
在实时中,当成功时,fork
() 系统调用返回子进程的进程标识符,父进程会返回非零值。出错时,它会返回一个错误号。
5.2. 线程
实时中可以存在多个线程。进程的所有线程共享其虚拟地址空间和系统资源。线程是一个可调度实体,其中包含:
- 程序计数器(PC)。
- 注册上下文。
- 堆栈指针。
在实时中,以下是创建并行性的潜在机制:
-
使用
fork ()
和exec ()
函数调用来创建新进程。fork
() 调用会创建一个进程的确切重复,它被调用并具有唯一的进程标识符。 -
使用 Posix 线程(
pthreads
) API 在已在运行的进程中创建新线程。
您必须在分叉实时线程前评估组件交互级别。当组件独立于另一个或者较少的交互时,创建新地址空间并将其作为新进程运行会很有用。当组件需要共享数据或频繁通信时,在一个地址空间内运行线程更高效。
在实时中,当成功时,fork
() 系统调用会返回零值。出错时,它会返回一个错误号。
第 6 章 RHEL for Real Time 上的应用程序时间戳
执行频繁 时间戳
的应用程序会受到读取时钟的 CPU 成本的影响。用于读取时钟的高成本和时间可能会对应用程序的性能造成负面影响。
您可以通过选择一个具有读机制的硬件时钟来降低读取时钟的成本,比默认时钟更快。
在 RHEL for Real Time 中,可以使用带有 clock_gettime ()
函数的 POSIX 时钟来获取更多性能,以生成可能最低 CPU 成本的时钟读取。
这些好处对于使用带有高读取成本的硬件时钟的系统更为明显。
6.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)。最后两个选项可昂贵地读取或具有低分辨率(时间粒度),因此它们适合用于实时内核。
6.2. POSIX 时钟
POSIX 是实施和代表时间源的标准。您可以为应用程序分配 POSIX 时钟,而不影响系统中的其他应用程序。这与内核选择并在系统中实施的硬件时钟相反。
用于读取给定 POSIX 时钟的函数是 clock_gettime ()
,它在 < time.h
> 中定义。内核与 clock_gettime ()
对应的是系统调用。当用户进程调用 clock_gettime ()
时:
-
对应的 C 库(
glibc
)调用sys_clock_gettime ()
系统调用。 -
sys_clock_gettime ()
执行请求的操作。 -
sys_clock_gettime ()
将结果返回给用户程序。
但是,从用户应用程序切换到内核的上下文具有 CPU 成本。虽然这个成本非常低,但如果操作重复了数千次,但累积的成本可能会对应用程序的整体性能产生影响。为了避免上下文切换到内核,从而更快地读取时钟,支持 CLOCK_MONOTONIC_COARSE
和 CLOCK_REALTIME_COARSE
POSIX 时钟,格式为虚拟动态共享对象(VDSO)库函数。
使用其中一个 _COARSE
时钟变体执行 clock_gettime ()
的时间读取不需要内核干预,且完全在用户空间中执行。这会显著提高性能。_COARSE
时钟读取的时间具有毫秒(ms)解析,这意味着不会记录小于 1 ms 的时间间隔。POSIX 时钟的 _COARSE
变体适合可容纳 millisecond 时钟分辨率的任何应用程序。
6.3. clock_gettime() function
以下代码显示了使用带有 CLOCK_MONOTONIC_COARSE
POSIX 时钟的 clock_gettime ()
函数的代码示例:
#include <time.h> main() { int rc; long i; struct timespec ts; for(i=0; i<10000000; i++) { rc = clock_gettime(CLOCK_MONOTONIC_COARSE, &ts); } }
#include <time.h>
main()
{
int rc;
long i;
struct timespec ts;
for(i=0; i<10000000; i++) {
rc = clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
}
}
您可以改进上面的示例,添加检查来验证 clock_gettime()
的返回值,验证 rc
变量的值,或确保 ts
结构的内容被信任。
clock_gettime ()
man page 提供了有关编写更可靠的应用程序的更多信息。
使用 clock_gettime()
函数的程序必须通过将 -lrt
添加到 gcc
命令行来与 rt
库相关联。
$ gcc clock_timing.c -o clock_timing -lrt
第 7 章 RHEL for Real Time 的调度策略
在实时中,调度程序是用来决定要运行的线程的内核组件。每个线程都有一个关联的调度策略和静态调度优先级,称为 sched_priority
。调度是抢占的,因此当具有较高静态优先级的线程准备好运行时,当前运行的线程将停止。然后,运行的线程会返回到其静态优先级的 waitlist
。
所有 Linux 线程都有以下调度策略之一:
-
SCHED_OTHER
或SCHED_NORMAL
: 是默认策略。 -
SCHED_BATCH
: 与SCHED_OTHER
类似,但具有增量性调整。 -
SCHED_IDLE
: 是优先级低于SCHED_OTHER
的策略。 -
SCHED_FIFO
: 是第一个 in 和第一个实时策略。 -
SCHED_RR
: 是循环实时策略。 -
SCHED_DEADLINE
: 是根据作业截止时间排列任务优先级的调度程序策略。最早的绝对期限的作业首先运行。
7.1. 调度程序策略
实时线程的优先级高于标准线程。策略具有调度优先级值,范围从最小值为 1 到最大值 99。
以下策略对实时至关重要:
SCHED_OTHER
或SCHED_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_FIFO
和SCHED_RR
线程运行,直到发生以下事件之一:- 线程进入睡眠状态或等待事件。
高优先级的实时线程准备好运行。
除非发生上述事件之一,否则线程在指定的处理器中无限期运行,而较低优先级线程保留在等待运行的队列中。这可能导致系统服务线程处于驻留状态,并阻止交换出文件系统数据并导致文件系统数据清除失败。
SCHED_DEADLINE
策略SCHED_DEADLINE
策略指定时间要求。它根据任务的截止时间调度每个任务。预计截止时间最早(EDF)调度的任务首先运行。内核需要
runtime swigdeadlineCloneperiod
变为 true。所需选项之间的关系是runtime swigdeadline swigperiod
。
7.2. SCHED_DEADLINE 策略的参数
每个 SCHED_DEADLINE
任务都是 以句点
、运行时和
截止时间
参数的特征。这些参数的值为纳秒的整数。
参数 | 描述 |
---|---|
|
例如,如果视频处理任务每秒处理 60 帧,每 16 毫秒为服务排队。因此, |
|
例如,如果视频处理工具在最糟糕的情况下,处理图像的五毫秒,则 |
|
例如,如果任务需要在十毫秒内提供已处理帧, |
7.3. 配置 SCHED_DEADLINE 参数
Red Hat Enterprise Linux 中的 sched_deadline_period_max_us
和 sched_deadline_period_min_us
参数是 SCHED_DEADLINE 调度策略的内核可调参数。这些参数通过使用此实时调度类来控制任务的最大允许周期(以微秒为单位)。
sched_deadline_period_max_us
和 sched_deadline_period_min_us
一起工作,为 SCHED_DEADLINE 任务的 period 值定义一个可接受的范围。
-
min_us
可防止可能使用过量资源的高频率任务。 -
max_us
会阻止极长的任务,它们可能会导致其他任务的性能。
使用参数的默认配置。如果需要更改参数的值,您必须在实时环境中配置自定义值前测试它们。
参数的值以微秒为单位。例如,1 秒等于 100000 微秒。
先决条件
- 您的系统必须具有 root 权限。
流程
使用其中一个
sysctl
命令临时设置所需的值。要使用
sched_deadline_period_max_us
参数,请运行以下命令:sysctl -w kernel.sched_deadline_period_max_us=2000000
# sysctl -w kernel.sched_deadline_period_max_us=2000000
Copy to Clipboard Copied! 要使用
sched_deadline_period_min_us
参数,请运行以下命令:sysctl -w kernel.sched_deadline_period_min_us=100
# sysctl -w kernel.sched_deadline_period_min_us=100
Copy to Clipboard Copied!
永久设置值。
对于
max_us
,编辑/etc/sysctl.conf
并添加以下行:kernel.sched_deadline_period_max_us = 2000000
kernel.sched_deadline_period_max_us = 2000000
Copy to Clipboard Copied! 对于
min_us
,请编辑/etc/sysctl.conf
并添加以下行:kernel.sched_deadline_period_min_us = 100
kernel.sched_deadline_period_min_us = 100
Copy to Clipboard Copied!
应用更改:
sysctl -p
# sysctl -p
Copy to Clipboard Copied!
验证
验证
max_us
的自定义值:cat /proc/sys/kernel/sched_deadline_period_max_us 2000000
$ cat /proc/sys/kernel/sched_deadline_period_max_us 2000000
Copy to Clipboard Copied! 验证
min_us
的自定义值:cat /proc/sys/kernel/sched_deadline_period_min_us 100
$ cat /proc/sys/kernel/sched_deadline_period_min_us 100
Copy to Clipboard Copied!
第 8 章 实时内核运行时验证
运行时验证是一种轻量级且严格的方法,用于检查系统事件及其正式规格之间的行为等效性。运行时验证有集成在附加到 tracepoints
的内核中的监控。如果系统状态与定义的规格不同,则运行时验证程序会激活响应器来通知或启用反应,如在日志文件或系统关闭时捕获事件以防止极端情况下的传播。
8.1. 运行时监控和响应器
运行时验证(RV)监控器封装在 RV 监控抽象中,并在定义的规格和内核追踪之间协调,以在 trace 文件中捕获运行时事件。RV 监控器包括:
- 参考模型是系统的参考模型。
- monitor 实例是 monitor 的一组实例,如每个 CPU 监视器或每个任务监控器。
- 将监控器连接到系统的帮助程序功能。
除了在运行时验证和监控系统外,您还可以启用对意外系统事件的响应。反应形式的反应可能与在 trace 文件中捕获事件不同,以启动非常反应,如关闭,以避免在安全关键系统上出现系统故障。
Reactors 是 RV 监视器可用的反应方法,根据需要定义对系统事件的反应。默认情况下,监控器提供操作的追踪输出。
8.2. 在线运行时监控器
运行时验证(RV)监控器分为以下类型:
在系统运行时,在线监控 trace 中的捕获事件。
如果事件处理附加到系统执行中,则在线监视器会同步。这将在事件监控期间阻止系统。如果执行与系统分离,且在不同机器上运行,在线监视器是异步的。但是,这需要保存的执行日志文件。
离线监控事件发生后生成的进程跟踪。
通过从持久性存储读取保存的 trace 日志文件,离线运行时验证捕获信息。只有在文件中保存了事件时,离线 monitor 才能正常工作。
8.3. 用户界面
用户界面位于 /sys/kernel/tracing/rv
,类似于追踪接口。用户界面包含上述文件和文件夹。
设置 | 描述 | 示例命令 |
---|---|---|
| 显示每行一个可用的监视器。 |
|
| 显示每行一个可用的响应器。 |
|
| 显示已启用的 monitor 每行一个。您可以同时启用多个 monitor。 使用 '!' 前缀编写 monitor 名称将禁用监控并截断文件会禁用所有已启用的 monitor。 |
|
|
|
|
|
使用 "[]" 中特定 MONITOR 选择的反应者列出可用的响应器。默认为 no operation ( 编写响应器的名称,将它集成到特定的 MONITOR 中。 |
|
|
在 trace 界面中启动
编写 | |
|
启用 reactors。编写 | |
| 显示 Monitor 描述 | |
|
显示 monitor 的当前状态。编写 |
第 9 章 RHEL for Real Time 中的关联性
在实时中,系统中的每个线程和中断源都有一个处理器关联性属性。操作系统调度程序使用此信息来确定在哪些 CPU 上运行哪些线程和中断。
实时的 Affinity 以位掩码表示,掩码中的每个位代表一个 CPU 内核。如果位设置为 1,则线程或中断可在该内核上运行;如果 0,线程或中断将排除在核心上运行。关联性位掩码的默认值为所有,这意味着线程或中断可以在系统的任意核心上运行。
默认情况下,进程可以在任何 CPU 上运行。但是,可以通过更改进程的关联性,指示进程在预先确定的 CPU 上运行。子进程继承其父进程的 CPU 相关性。
一些更典型的关联性设置包括:
- 为所有系统进程保留一个 CPU 内核,并允许应用程序在内核的其余部分上运行。
-
在同一 CPU 上允许线程应用程序和给定内核线程(如网络
softirq
或驱动程序线程)。 - 对每个 CPU 上的生成者和消费者线程配对。
关联性设置必须与程序结合使用,以获得良好的预期行为。
9.1. 处理器关联性
在实时中,进程默认可在任何 CPU 上运行。但是,您可以通过更改进程的关联性,将进程配置为在预先确定的 CPU 上运行。子进程继承其父进程的 CPU 相关性。
在系统上调优关联性的实时做法是确定运行应用程序所需的内核数,然后隔离这些内核。这可以通过 Tuna 工具或 shell 脚本修改 bitmask 值来实现。
taskset
命令可用于更改进程的关联性,并修改 /proc/
文件系统条目会更改中断的关联性。使用带有 a -p
or- pid 选项和
进程的进程标识符(PID)的 taskset
命令,检查进程的关联性。
-c
or -cpu-list
选项显示内核的数字列表,而不是显示为位掩码。可以通过指定要绑定特定进程的 CPU 数量来设置关联性。例如,对于之前使用 CPU 0 或 CPU 1 的进程,您可以更改关联性,使其只能在 CPU 1 上运行。除了 taskset
命令外,您还可以使用 sched_setaffinity ()
系统调用来设置处理器关联性。
9.2. SCHED_DEADLINE 和 cpusets
内核截止时间调度类(SCHED_DEADLINE
)为具有受限期限的 sporadic 任务实施早期期限第一个调度程序(EDF)。它根据作业截止时间对任务进行优先排序:首先最早的绝对期限。除了 EDF 调度程序外,截止时间调度程序还实施恒定带宽服务器(CBS)。CBS 算法是一个资源保留协议。
CBS 保证每个任务在每个期间 (T)
接收其运行时 (Q)
。每次激活一个任务时,CBS 会清除任务的运行时间。当作业运行时,它会消耗其 运行时
,如果任务没有运行时,则任务会节流和取消调度。节流机制可防止单个任务运行超过其运行时,有助于避免其他作业的性能问题。
在实时,为了避免使用 截止时间
任务过载系统,截止时间调度程序
会实施验收测试,当任务配置为使用 截止时间调度程序
运行时都会运行。接受测试保证 SCHED_DEADLINE
任务不使用比 kernel.sched_rt_runtime_us
/kernel.sched_rt_period_us
文件(默认为 1s 950 ms)更多的 CPU 时间。
第 10 章 RHEL for Real Time 中的线程同步机制
实时,当两个或多个线程同时访问共享资源时,线程会协调使用线程同步机制。线程同步可确保一次只有一个线程使用共享资源。Linux 中使用的三个线程同步机制: Mutexes、Barriers 和 Condition 变量(condvars
)。
10.1. mutexes
Mutex 从相互排除的术语中派生出来。mutual exclude 对象同步对资源的访问。它是保证一个线程一次只能获取 mutex 的机制。
mutex
算法会创建一个对代码的每个部分的串行访问,以便在任何时候都执行代码。mutex
使用称为 mutex 属性对象的属性对象创建。这是一个抽象对象,其中包含多个取决于您选择实现的 POSIX 选项的属性。属性对象通过 pthread_mutex_t
变量定义。对象存储为 mutex 定义的属性。pthread_mutex_init (&my_mutex, &my_mutex_attr)
, pthread_mutexattr_setrobust ()
和 pthread_mutexattr_getrobust ()
函数返回 0。出错时,它们会返回错误号。
在实时中,您可以保留属性对象来初始化同一类型的更多静默,或者您可以清理(销毁)属性对象。mutex 在这两种情况下都不会受到影响。mutexes 包括标准和高级 mutexes。
Standard mutexes
实时标准 mutexes 是私有、非递归、非忙碌和非优先级继承能力。使用 pthread_mutex_ init (&my_mutex, &my_mutex_attr)初始化
创建一个标准的 mutex。使用标准 mutex 类型时,您的应用程序可能无法从 pthread_mutex_
tpthreads
API 和 RHEL for Real Time 内核提供的优点中受益。
Advanced mutexes
带有额外功能的 mutexes 称为高级 mutexes。高级功能包括 mutex 的优先级继承、可靠的行为,以及共享和私有 mutexes。例如,对于强大的 mutexes,初始化 pthread_mutexattr_setrobust ()
函数,设置 robust 属性。同样,通过使用属性 PTHREAD_PROCESS_SHARED
,允许任何线程在 mutex 上运行,只要线程有权访问其分配的内存。属性 PTHREAD_PROCESS_PRIVATE
设置私有 mutex。
非滥用 mutex 不会自动发布并保持锁定,直到手动释放为止。
10.2. 障碍
与其他线程同步方法相比,障碍的运行方式截然不同。障碍在代码中定义一个点,其中所有活动线程都停止,直到所有线程和进程都到达这个障碍。当运行的应用程序需要确保所有线程都已完成特定任务时,可以在继续操作前,使用障碍。
barrier mutex 实时使用以下两个变量:
-
第一个变量记录了
障碍的停止和
传递
状态。 - 第二个变量记录了进入障碍的线程总数。
只有指定数量的线程数量达到定义的 barrier 时,barrier 才会将状态设置为 pass
。当 barrier 状态设置为 pass
时,线程和进程将继续。pthread_barrier_init ()
函数分配所需的资源,以使用定义的 barrier,并使用 attr
属性对象引用的属性进行初始化。
当成功时,pthread_barrier_init ()
和 pthread_barrier_destroy ()
函数返回零值。出错时,它们会返回错误号。
10.3. 条件变量
在实时中,条件变量(condvar
)是一个 POSIX 线程结构,在继续操作前等待实现特定条件。通常,信号条件与线程与其他线程共享的数据状态相关。例如,condvar
可用于向处理队列的数据条目发送信号,以及等待处理队列中数据的线程。通过使用 pthread_cond_init ()
函数,您可以初始化条件变量。
当成功时,pthread_cond_init ()
、pthread_cond_wait ()
和 pthread_cond_signal ()
函数返回零值。出错时,它会返回错误号。
10.4. mutex 类
上述 mutex 选项提供了编写或移植应用程序时需要考虑的 mutex 类的指导。
Advanced mutexes | 描述 |
---|---|
共享 mutexes |
定义多个线程的共享访问权限,以在给定时间获取 mutex。共享 mutexes 可以创建延迟。属性是 |
Private mutexes |
确保只有在同一进程中创建的线程才能访问 mutex。属性是 |
实时优先级继承 |
设置优先级优先级高于当前优先级任务的优先级级别。当任务完成后,它会释放资源,任务会丢弃回其原始优先级,以允许运行更高的优先级任务。属性是 |
强大的互斥器 |
将可靠的 mutexes 设置为在拥有线程停止时自动发布。字符串 |
10.5. 线程同步功能
上述函数类型和描述列表提供了用于实时内核线程同步机制的功能信息。
功能 | 描述 |
---|---|
|
使用由 |
|
销毁指定的 mutex 对象。您可以使用 |
|
指定 mutex 的 |
|
查询 mutex 的 |
|
分配所需的资源,以使用属性对象 |
|
初始化 condition 变量。 |
|
阻止线程执行,直到它从另一个线程接收信号。另外,对这个功能的调用也会在阻塞前在 mutex 上释放相关的锁定。参数 |
|
至少在指定条件变量中阻止的一个线程的 Unblocks。参数 |
第 11 章 RHEL for Real Time 中的套接字选项
实时套接字是同一系统上两个进程(如 UNIX 域和环回设备)或不同系统上(如网络套接字)之间的两种数据传输机制。
传输控制协议(TCP)是最常见的传输协议,通常用于为需要恒定通信的服务实现一致的低延迟,或在低优先级限制环境中的套接字合并。
随着新应用、硬件功能和内核架构优化,TCP 必须引入新方法来有效地处理更改。新方法可能会导致不稳定的程序行为。因为程序的行为因底层操作系统组件的变化而改变,因此必须谨慎处理。
TCP 中这种行为的一个示例是发送小缓冲区的延迟。这允许将它们作为网络数据包发送。将小的写入缓冲到 TCP,并一次性全部发送,但也可以创建延迟。对于实时应用程序,TCP_NODELAY
套接字选项会禁用延迟,并在就绪后马上发送小写入。
数据传输的相关套接字选项为 TCP_NODELAY
和 TCP_CORK
。
11.1. TCP_NODELAY 套接字选项
TCP_NODELAY
套接字选项禁用 Nagle 的算法。使用 setsockopt
socket API 功能配置 TCP_NODELAY
会在单个数据包就绪后立即发送多个小缓冲区写入。
在发送前,通过构建连续数据包,将多个逻辑上相关的缓冲区作为单一数据包发送,从而获得更高的延迟和性能。或者,如果内存缓冲区在逻辑上相关,但不是连续的,您可以创建一个 I/O 向量,并在启用了 TCP_NODELAY
的套接字上使用 writev
传递给内核。
以下示例演示了通过 setsockopt
socket API 启用 TCP_NODELAY
。
int one = 1; setsockopt(descriptor, SOL_TCP, TCP_NODELAY, &one, sizeof(one));
int one = 1;
setsockopt(descriptor, SOL_TCP, TCP_NODELAY, &one, sizeof(one));
要有效地使用 TCP_NODELAY
,请避免在逻辑上相关的缓冲区写入。使用 TCP_NODELAY
时,小写入会导致 TCP 发送多个缓冲区作为单独的数据包,这可能会导致整体性能不佳。
11.2. TCP_CORK 套接字选项
TCP_CORK
选项收集套接字中的所有数据包,并防止传输它们,直到缓冲区填满了指定的限制。这可让应用在内核空间中构建数据包,并在禁用 TCP_CORK
时发送数据。使用 setsocketopt ()
函数在套接字文件描述符上设置 TCP_CORK
。在开发程序时,如果必须从文件中发送批量数据,请考虑使用带有 sendfile ()
函数的 TCP_CORK
。
当由各种组件在内核中构建逻辑数据包时,使用 setsockopt
socket API 将其配置为 1 值来启用 TCP_CORK
。这称为 "corking the socket"。如果 cork 在适当的时间没有移除,TCP_CORK
可能会导致错误。
以下示例演示了通过 setsockopt
socket API 启用 TCP_CORK
。
int one = 1; setsockopt(descriptor, SOL_TCP, TCP_CORK, &one, sizeof(one));
int one = 1;
setsockopt(descriptor, SOL_TCP, TCP_CORK, &one, sizeof(one));
在有些环境中,如果内核无法识别何时删除 cork,您可以手动删除它,如下所示:
int zero = 0; setsockopt(descriptor, SOL_TCP, TCP_CORK, &zero, sizeof(zero));
int zero = 0;
setsockopt(descriptor, SOL_TCP, TCP_CORK, &zero, sizeof(zero));
11.3. 使用 socket 选项的程序示例
TCP_NODELAY
和 TCP_CORK
套接字选项会影响网络连接的行为。TCP_NODELAY
在应用程序上禁用 Nagle 的算法,这些算法可以在数据数据包就绪后立即发送。使用 TCP_CORK
,您可以同时传输多个数据数据包,它们之间没有延迟。
要启用套接字选项,如 TCP_NODELAY
,请使用以下代码构建它,然后设置适当的选项。
gcc tcp_nodelay_client.c -o tcp_nodelay_client -lrt
gcc tcp_nodelay_client.c -o tcp_nodelay_client -lrt
当您运行 tcp_nodelay_server
和 tcp_nodelay_client
程序时,客户端会使用默认的套接字选项。有关 tcp_nodelay_server
和 tcp_nodelay_client
程序的更多信息,请参阅红帽知识库解决方案 TCP 更改会在使用小缓冲区时造成延迟性能。
示例程序提供有关这些套接字选项对应用程序影响的性能的信息。
对客户端的性能影响
您可以在不使用 TCP_NODELAY
和 TCP_CORK
套接字选项的情况下,向客户端发送小缓冲区写入。当不带任何参数运行时,客户端使用默认的套接字选项。
要启动数据传输,请定义服务器 TCP 端口:
./tcp_nodelay_server 5001
$ ./tcp_nodelay_server 5001
Copy to Clipboard Copied! 代码发送 15 个数据包,每个数据包有两个字节,并等待来自服务器的响应。它在此处采用默认的 TCP 行为
对回环接口的性能影响
要启用 socket 选项,使用 gcc tcp_nodelay_client.c -o tcp_nodelay_client -lrt
来构建它,然后设置适当的选项。
以下示例使用回环接口来演示三种变体:
要立即发送缓冲区写入,请在配置了
TCP_NODELAY
的套接字上设置no_delay
选项。./tcp_nodelay_client localhost --port=5001 --nr_logical_packets=10000 --no_delay --verbose
$ ./tcp_nodelay_client localhost --port=5001 --nr_logical_packets=10000 --no_delay --verbose 10000 packets (100 buffers) sent in 4079.655518 ms: 490.237457 bytes/ms using TCP_NODELAY
Copy to Clipboard Copied! TCP 正确发送缓冲区,从而禁用组合小数据包的算法。这提高了性能,但可能会导致为每个逻辑数据包发送小数据包。
要收集多个数据包并通过一个系统调用发送它们,请配置
TCP_CORK
套接字选项。/tcp_nodelay_client localhost --port=5001 --nr_logical_packets=10000 --cork --verbose
$ /tcp_nodelay_client localhost --port=5001 --nr_logical_packets=10000 --cork --verbose 10000 packets (100 buffers) sent in 669.514221 ms: 2987.240479 bytes/ms using TCP_CORK
Copy to Clipboard Copied! 使用 cork 技术可以显著减少发送数据包所需的时间,因为它在其缓冲区中组合了完整的逻辑数据包,并发送较少的总体网络数据包。您必须确保在适当的时间删除
cork
。在开发程序时,如果必须从文件中发送批量数据,请考虑使用带有
sendfile ()
选项的TCP_CORK
。在不使用套接字选项的情况下测量性能。
./tcp_nodelay_client localhost --port=5001 --nr_logical_packets=10000 --verbose
$ ./tcp_nodelay_client localhost --port=5001 --nr_logical_packets=10000 --verbose 10000 packets (100 buffers) sent in 410403.718750 ms: 4.873250 bytes/ms
Copy to Clipboard Copied! 这是 TCP 组合缓冲区写入并等待数据检查数据比优化在网络数据包中时的基准测量。
第 12 章 RHEL for Real Time 调度程序
RHEL for Real Time 使用命令行工具帮助您配置和监控进程配置。
12.1. 用于设置调度程序的 chrt 工具
chrt
工具检查并调整调度程序策略和优先级。它可以启动具有所需属性的新进程,或更改正在运行的进程的当前属性。
chrt
工具使用 either-- pid
或 the -p
选项来指定进程 ID (PID)。
chrt
工具采用以下策略选项:
-
-f
or-fifo
: 将调度设置为SCHED_FIFO
。 -
-o
or--other
:将调度设置为SCHED_OTHER
。 -
-r
or--rr
:将调度设置为SCHED_RR
. -
-d
or-deadline
: 将调度设置为SCHED_DEADLINE
.
以下示例显示了指定进程的属性。
chrt -p 468
# chrt -p 468
pid 468’s current scheduling policy: SCHED_FIFO
pid 468’s current scheduling priority: 85
12.2. 抢占调度
实时抢占是临时中断执行任务的机制,目的是以后恢复该任务。当优先级较高的进程中断 CPU 用量时,会发生它。抢占可能会对性能有负面影响,持续抢占可能会导致状态称为 thrashing。当进程被持续抢占且没有完全运行任何进程时,会出现这个问题。更改任务的优先级有助于减少非自愿抢占。
您可以通过查看 /proc/PID/status
文件的内容来检查在单个进程上发生的 voluntary 和非自愿抢占,其中 PID 是进程标识符。
以下示例显示了 PID 为 1000 的进程的抢占状态。
grep voluntary /proc/1000/status
# grep voluntary /proc/1000/status
voluntary_ctxt_switches: 194529
nonvoluntary_ctxt_switches: 195338
12.3. 调度程序优先级的库函数
实时进程使用一组不同的库调用来控制策略和优先级。函数需要包含 sched.h
头文件。符合 SCHED_OTHER
, SCHED_RR
和 SCHED_FIFO
也需要在 sched.h
header 文件中定义。
表列出了为实时调度程序设置策略和优先级的功能。
Functions | 描述 |
---|---|
| 检索特定进程标识符(PID)的调度程序策略. |
|
设置调度程序策略和其他参数。此功能需要三个参数: |
| 检索调度策略的调度参数。 |
|
设置与已设置的调度策略关联的参数,并可使用 |
| 返回与调度策略关联的最大有效优先级。 |
| 返回与调度策略关联的最小有效优先级。 |
|
显示每个进程的分配 |
第 13 章 RHEL for Real Time 中的系统调用
实时系统调用是应用程序程序用来与内核通信的函数。它是程序从内核订购资源的机制。
13.1. sched_yield() function
sched_yield ()
函数为处理器设计,用于选择与正在运行的进程以外的进程。在较差的应用程序中发出不良的应用程序时,这种类型的请求容易出错。
当在具有实时优先级的进程中使用 sched_yield ()
函数时,它可能会显示意外行为。调用 sched_yield ()
的进程将移到在同一优先级运行的进程队列的尾部。如果没有以相同优先级运行其他进程,则名为 sched_yield ()
的进程会继续运行。如果该进程的优先级很高,它可能会创建一个忙碌的循环,从而导致机器无法使用。
通常,不要在实时进程中使用 sched_yield ()
。
13.2. getrusage ()function
getrusage ()
函数从指定的进程或线程检索重要信息。它报告了以下信息,例如:
- voluntary 和 involuntary 上下文切换的数量。
- 主要和次要页面错误.
- 使用的内存量。
getrusage ()
允许您查询应用程序,以提供与性能调优和调试活动相关的信息。getrusage ()
检索信息,否则需要从 /proc/
目录中的多个不同文件进行目录,并难以与应用程序上的特定操作或事件同步。
内核不会设置填充 getrusage ()
结果中的结构中的所有字段。其中一些仅出于兼容性原因而保留。
第 14 章 使用 RHEL for Real Time 中的 timerlat 测量调度延迟
rtla-timerlat
工具是 timerlat
追踪器的接口。timerlat
追踪器查找实时线程的 wake-up 延迟源。timerlat
追踪器为每个 CPU 创建一个具有实时优先级的内核线程,这些线程将定期计时器设置为 wake,并返回到 sleep。在唤醒时,timerlat
会查找并收集信息,这对于调试操作系统计时器延迟非常有用。timerlat
追踪器生成输出,并在每次激活时打印以下两行:
-
timerlat
追踪程序定期打印计时器中断请求(IRQ)处理程序中看到的计时器延迟。这是在线程激活前在hardirq
上下文中看到的第一个输出。 -
第二个输出是线程的计时器延迟。
ACTIVATION ID
字段显示中断请求(IRQ)性能,以及它们对应的线程执行。
14.1. 配置 timerlat 追踪器来测量调度延迟
您可以通过在追踪系统的 curret_tracer
文件中添加 timerlat
来配置 timerlat
追踪器。current_tracer
文件通常挂载到 /sys/kernel/tracing
目录中。timerlat
追踪器测量中断请求(IRQ),并在线程延迟超过 100 微秒时保存用于分析的 trace 输出。
流程
列出当前的 tracer:
cat /sys/kernel/tracing/current_tracer nop
# cat /sys/kernel/tracing/current_tracer nop
Copy to Clipboard Copied! 没有操作
(nop
)是默认的 tracer。在追踪系统的
current_tracer
文件中添加timerlat
追踪器:cd /sys/kernel/tracing/ echo timerlat > current_tracer
# cd /sys/kernel/tracing/ # echo timerlat > current_tracer
Copy to Clipboard Copied! 生成追踪输出:
cat trace tracer: timerlat
# cat trace # tracer: timerlat
Copy to Clipboard Copied!
验证
输入以下命令检查
timerlat
是否作为当前的 tracer 启用:cat /sys/kernel/tracing/current_tracer timerlat
# cat /sys/kernel/tracing/current_tracer timerlat
Copy to Clipboard Copied!
14.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 处理程序超过配置的值。
14.3. 使用 rtla-timerlat-top 测量计时器延迟
rtla-timerlat-top
tracer 显示 timerlat tracer
中定期输出的摘要。tracer 输出还提供有关每个操作系统声明和事件的信息,如 osnoise
和 追踪点
。您可以使用 the -t
选项查看此信息。
流程
测量计时器延迟:
rtla timerlat top -s 30 -T 30 -t
# rtla timerlat top -s 30 -T 30 -t
Copy to Clipboard Copied!
14.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)中保存堆栈跟踪。
第 15 章 使用 RHEL for Real Time 中的 rtla-osnoise 测量调度延迟
低延迟是一个环境,它经过优化来处理具有低容错(延迟)的大量数据。为应用程序提供专用资源(包括 CPU)是在大量低延迟环境中进行的。例如,对于网络功能虚拟化(NFV)应用中的高性能网络处理,单个应用具有 CPU 电源限制集来持续运行任务。
Linux 内核包含实时分析(rtla
)工具,它为操作系统 noise (osnoise
) tracer 提供了一个接口。操作系统声明是应用中因操作系统内活动而进行的干扰。Linux 系统可能会因为以下原因而体验:
- 不可屏蔽中断(NMI)
- 中断请求(IRQ)
- 软中断请求(SoftIRQ)
- 其他系统线程活动
- 与硬件相关的作业,如不可屏蔽的高优先级系统管理中断(SMI)
15.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 源的计数器。
15.2. 配置 rtla-osnoise tracer 以测量调度延迟
您可以通过在追踪系统的 curret_tracer
文件中添加 osnoise
来配置 rtla-osnoise
tracer。current_tracer
文件通常挂载到 /sys/kernel/tracing/
目录中。rtla-osnoise
追踪器测量中断请求(IRQ),并在线程延迟超过 20 微秒内保存分析的 trace 输出。
流程
列出当前的 tracer:
cat /sys/kernel/tracing/current_tracer nop
# cat /sys/kernel/tracing/current_tracer nop
Copy to Clipboard Copied! 没有操作
(nop
)是默认的 tracer。在追踪系统的
current_tracer
文件中添加timerlat
追踪器:cd /sys/kernel/tracing/ echo osnoise > current_tracer
# cd /sys/kernel/tracing/ # echo osnoise > current_tracer
Copy to Clipboard Copied! 生成追踪输出:
cat trace tracer: osnoise
# cat trace # tracer: osnoise
Copy to Clipboard Copied!
15.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 微秒。
15.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 持续时间。
15.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。
15.6. 使用 rtla-osnoise-top tracer 测量操作系统noise
rtla osnoise-top
tracer 测量并显示 osnoise
tracer 的定期摘要,以及有关 interference 源发生计数器的信息。
流程
测量系统 noise:
rtla osnoise top -P F:1 -c 0-3 -r 900000 -d 1M -q
# rtla osnoise top -P F:1 -c 0-3 -r 900000 -d 1M -q
Copy to Clipboard Copied! 命令输出显示定期摘要,其中包含有关实时优先级的信息、要运行线程的 CPU 以及以微秒为单位运行的周期。
15.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 < ;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
- 打印帮助菜单。
第 16 章 在实时内核和解决方案中调度问题
实时内核中的调度有时可能会导致。通过使用提供的信息,您可以了解调度策略、调度程序节流和线程不足状态的问题,以及潜在的解决方案。
16.1. 实时内核的调度策略
实时调度策略共享一个主要特征:它们运行直到高优先级线程通过休眠或执行 I/O 中断线程或线程等待。
对于 SCHED_RR
,操作系统会中断一个正在运行的线程,以便相同 SCHED_RR
优先级的另一个线程可以运行。在这两种情况下,POSIX
规格不会进行置备,该规范定义了允许较低优先级线程获得任何 CPU 时间的策略。实时线程的这种特性意味着,可轻松编写应用程序,这会对给定 CPU 的 100% 进行单调。但是,这会导致操作系统出现问题。例如,操作系统负责管理系统范围和每个 CPU 资源,必须定期检查描述这些资源的数据结构,并使用它们执行内务操作。但是,如果内核被 SCHED_FIFO
线程 monopolized,则无法执行其内务任务。最终,整个系统变得不稳定,并可能导致崩溃。
在 RHEL for Real Time 内核中,中断处理程序作为具有 SCHED_FIFO
优先级的线程运行。默认优先级为 50。高于中断处理器线程的 SCHED_FIFO
或 SCHED_RR
策略的 cpu-hog 线程可能会阻止中断处理程序运行。这会导致程序等待这些中断信号的数据,并失败。
16.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 时间。只有当实时任务经过精心设计且没有明显的注意事项(如未绑定的轮询循环)时,这才有效。
16.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
-