版权声明:本文为本文为博主原创文章,转载请注明出处。如有错误,欢迎指正。
@
本文讲述一些有利于提高xenomai 实时性配置建议,其中,一些是通用的,一些是针对某个架构的,希望对你有用。
一、影响因素
硬实时操作系统应具备的最重要特性之一是可预测性,系统应该保证满足所有关键时序约束。然而,这取决于一系列因素,这些因素涉及硬件的架构特征、内核中采用的机制和策略,以及用于实现应用程序的编程语言。
1.硬件
硬件方面,第一个影响调度可预测性的是处理器本身。处理器的内部特性是不确定性的第一个原因,例如指令预取、流水线操作、高速缓存存储器和直接存储器访问(DMA)机制。这些特性虽然改善了处理器的平均性能,但它们引入了非确定性因素,这些因素阻止了对最坏情况执行时间(WCET)的精确估计。
其他影响因素有内存、散热。提升内存频率可降低内存访问延时;使用双通道内存,这两个内存CPU可分别寻址、读取数据,从而使内存的带宽增加一倍,数据存取速度也相应增加一倍(理论上),内存访问延时得到缩短,进而提升系统的实时性能;处理器散热设计不好,温度过高时会引发CPU降频保护,系统运行频率降低影响实时性,热设计应确保在高工作量时的温度不会引发降频。
对于X86 CPU,双通道内存性能是单通道内存的2. 5倍以上;正确的热设计可使实时性提升1.4倍以上。
2.BISO(X86平台)
BISO需要针对实时系统进行配置。优化的BIOS设置与使用默认BISO设置的实时性能差距高达9倍。
3.软件
操作系统:调度算法,同步机制,信号量类型,内存管理策略,通信语义和中断处理。
资源的分配隔离:分配CPU专门对实时任务服务、将多余中断隔离到非实时任务CPU上,分配CPU专门对实时任务服务可使L1 、L2 Cache只为实时任务服务。
实时任务的设计,良好的软件设计能更好的发挥实时性能。
其他,虚拟化、GUI等
4. 缓存使用策略与GPU
CPU 里的 L1 Cache 或者 L2 Cache,访问延时是内存的 1/15 乃至 1/100,想要追求极限性能,需要尽可能地多从 CPU Cache 里面拿数据,减少cache miss,上面的分配CPU专门对实时任务服务就是对非共享的L1 、L2 Cache的充分优化。对于L3 Cache,多个cpu核与GPU共享,无法避免非实时任务及GUI争抢L3 Cache对实时任务的影响。为此intel 对Last Level Cache 推出了CAT(缓存分配技术,Cache Alocation Technology),对最后一级缓存(L3 Cache)实现分区,用户可以通过限制每个核心能够向其中分配缓存行的LLC数量,将LLC的部分分配给特定核心,使用该技术可以提升实时任务Cahe命中率,减少MSI延迟和抖动,进而提升实时性能。(不是所有intel处理器具有该功能,关于CAT 见github)
GPU。硬件上GPU与CPU共享L3 Cache ,因此GUI会影响实时任务的实时性。intel建议根据GUI任务的工作负载来固定GPU的运行频率,且频率尽可能低。减小GPU对实时任务实时性的影响。
二、优化措施
1. BIOS[x86]
Disable Features | Intela Hyper-Threading Technology. Intel SpeedStep. Intel Speed Shift Technology C-States: Gfx RC6. GT PM Support. PCH Cross Throttling. PCI Express* Clock Gating. Delay Enable DMI ASPM,DMI Link ASPM Control. PCle *ASPM and SATA Aggressive LPM Support. (For Skylake and Kaby Lake, also consider disabling Gfx Low Power Mode and USB Periodic SMl in BIOS.) |
Enable Features | Legacy lO Low Latency |
Gfx Frequency | Set to fixed value as low as possible according to proper workload |
Memory Frequency | SA GV Fixed High |
2. 硬件
除处理器外,内存方面,使用双通道内存,尽可能高的内存频率。
散热当面,针对处理器工作负载设计良好的散热结构。
3. Linux
xenomai基于linux,xenomai作为一个小的实时核存在,许多硬件配置是linux 驱动掌管的,必须让linux配置好,给xenomai提供一个好的硬件环境,让xenomai充分发挥其RTOS的优势,主要宗旨:尽可能的不让linux影响xenomai,无论是软件还是硬件。
3.1 Kernel CMDLINE
cpu隔离
多核情况下将CPU隔离,设置内核参数isolcpus=[cpu列表]
,将列表中的CPU从内核SMP平衡和调度算法中剔除,将剔除的CPU用于RT应用。如4核CPU平台将第3、4核隔离来做RT应用。
GRUB_CMDLINE_LINUX="isolcpus=2,3"
以上只是linux不会调度普通任务到CPU2和3上运行,这是基础,此时还需要设置xenomai方面的CPU隔离,方法一,任务通过函数pthread_attr_setaffinity_np()
设置xenomai任务只在CPU3和4上调度,隔离后的CPU的L1、L2缓存命中率相应的也会得到提高。
cpu_set_t cpus;
CPU_ZERO(&cpus);
CPU_SET(2, &cpus);//将线程限制在指定的cpu2上运行
CPU_SET(3, &cpus);//将线程限制在指定的cpu3上运行
ret = pthread_attr_setaffinity_np(&tattr, sizeof(cpus), &cpus);
方法二,向xenomai设置内核参数supported_cpus
,指定xenomai支持的CPU,xenomai任务会自动放到cpu2、cpu3上运行。
GRUB_CMDLINE_LINUX="isolcpus=2,3 xenomai.supported_cpus=0x06"
注:linux内核参数isolcpus=CPU编号列表
是基础,否则若不隔离linux任务,后面的xenomai设置将没任何意义。
Full Dynamic Tick
将CPU2、CPU3作为xenomai使用后,由于xenomai调度是完全基于优先级的调度器,并且我们已将linux任务从这两个cpu上剔除,CPU上Tick也就没啥用了,避免多余的Tick中断影响实时任务的运行,需要将这两个cpu配置为Full Dynamic Tick模式,即关闭tick。通过添加linux内核参数nohz_full=[cpu列表]
配置。
nohz_full=[cpu列表]
在使用CONFIG_NO_HZ_FULL = y
构建的内核中才生效。
GRUB_CMDLINE_LINUX="isolcpus=2,3 xenomai.supported_cpus=0x06 nohz_full=2,3"
为什么是linux内核参数呢?双核下时间子系统中分析过,每个CPU的时钟工作方式是linux初始化并配置工作模式的,xenomai最后只是接管而已,所以这里是通过linux内核参数配置。
注意:boot CPU(通常是0号CPU)会无条件的从列表中剔除。这是一个坑~
Offload RCU callback
从引导选择的CPU上卸载RCU回调处理,使用内核线程 “rcuox / N”代替,通过linux内核参数rcu_nocbs=[cpu列表]
指定的CPU列表设置。这对于HPC和实时工作负载很有用,这样可以减少卸载RCU的CPU上操作系统抖动。
rcu_nocbs=[cpu列表]
在使用CONFIG_RCU_NOCB_CPU=y
构建的内核中才生效。除此之外需要设置RCU内核线程rcuc/n
和rcub/n
线程的SCHED_FIFO优先级值RCU_KTHREAD_PRIO,RCU_KTHREAD_PRIO设置为高于最低优先级线程的优先级,也就是说至少要使该优先级低于xenomai实时应用的优先级,避免xenomai实时应用迁移到linux后,由于优先级低于RCU_KTHREAD的优先级而实时性受到影响,如下配置RCU_KTHREAD_PRIO=0。
General setup --->
RCU Subsystem --->
(0) Real-time priority to use for RCU worker threads
[*] Offload RCU callback processing from boot-selected CPUs
(X) No build_forced no-CBs CPUs
( ) CPU 0 is a build_forced no-CBs CPU
( ) All CPUs are build_forced no-CBs CPUs
GRUB_CMDLINE_LINUX="isolcpus=2,3 xenomai.supported_cpus=0x06 nohz_full=2,3 rcu_nocbs=2,3"
中断隔离
多核情况下,通过内核参数irqaffinity==[cpu列表]
,设置linux中断的亲和性,设置后,默认由这些cpu核来处理非CPU绑定中断。避免linux中断影响cpu2、cpu3上的实时应用,将linux中断指定到cpu0、cpu1处理。
GRUB_CMDLINE_LINUX="isolcpus=2,3 xenomai.supported_cpus=0x06 nohz_full=2,3 rcu_nocbs=2,3 irqaffinity=0,1"
以上只是设置linux中断的affinity,只能使运行实时任务的CPU2、cpu3不会收到linux非CPU绑定中断请求。
要指定cpu来处理xenomai实时设备中断,需要在实时驱动代码中通过函数xnintr_affinity()
设置,绑定实时驱动中断由CPU2、CPU3处理代码如下。
cpumask_t irq_affinity;
...
cpumask_clear(&irq_affinity);
cpumask_set_cpu(2, &irq_affinity);
cpumask_set_cpu(3, &irq_affinity);
...
if (!cpumask_empty(&irq_affinity)){
xnintr_affinity(&pIp->irq_handle,irq_affinity); /*设置实时设备中断的affinity*/
}
虽然ipipe会保证xenomai 实时中断在任何CPU都会优先处理,在实时设备中断比较少的场合,我觉得把linux中断与实时中断分开比较好;如果实时设备中断数量较多,如果隔离就会造成实时中断间相互影响中断处理的实时性,这时候不指定实时中断处理CPU比较好。
禁用irqbanlance
irqbalance 用于优化中断分配,它会自动收集系统数据以分析使用模式,并依据系统负载状况将工作状态置于 Performance mode 或 Power-save mode。简单来说irqbalance 会将硬件中断分配到各个CPU核心上处理。
- 处于 Performance mode 时,irqbalance 会将中断尽可能均匀地分发给各个 CPU core,以充分利用 CPU 多核,提升性能。
- 处于 Power-save mode 时,irqbalance 会将中断集中分配给第一个 CPU,以保证其它空闲 CPU 的睡眠时间,降低能耗。
禁用irqbanlance,避免不相干中断发生在RT任务核。发行版不同,配置方式不同,以Ubuntu为例,停止/关闭开机启动如下。
systemctl stop irqbalance.service
systemctl disable irqbalance.service
必要的话直接卸载irqbalance。
apt-get remove irqbalance
x86平台还可添加参数acpi_irq_nobalance
禁用ACPI irqbalance.
GRUB_CMDLINE_LINUX="isolcpus=2,3 xenomai.supported_cpus=0x06 nohz_full=2,3 rcu_nocbs=2,3 irqaffinity=0,1 acpi_irq_nobalance noirqbalance"
intel 核显配置[x86]
主要针对intel CPU的核显,配置intel核显驱动模块i915,内核参数如下。
GRUB_CMDLINE_LINUX="i915.enable_rc6=0 i915.enable_dc=0 i915.disable_power_well=0 i915.enable_execlists=0 i915.powersave=0"
nmi_watchdog[x86]
NMI watchdog是Linux的开发者为了debugging而添加的特性,但也能用来检测和恢复Linux kernel hang,现代多核x86体系都能支持NMI watchdog。
NMI(Non Maskable Interrupt)即不可屏蔽中断,之所以要使用NMI,是因为NMI watchdog的监视目标是整个内核,而内核可能发生在关中断同时陷入死循环的错误,此时只有NMI能拯救它。
Linux中有两种NMI watchdog,分别是I/O APIC watchdog(nmi_watchdog=1)和Local APIC watchdog(nmi_watchdog=2)。它们的触发机制不同,但触发NMI之后的操作是几乎一样的。一旦开启了I/O APIC watchdog(nmi_watchdog=1),那么每个CPU对应的Local APIC的LINT0线都关联到NMI,这样每个CPU将周期性地接到NMI,接到中断的CPU立即处理NMI,用来悄悄监视系统的运行。如果系统正常,它啥事都不做,仅仅是更改 一些时间计数;如果系统不正常(默认5秒没有任何普通外部中断),那它就闲不住了,会立马跳出来,且中止之前程序的运行。该出手时就出手。
避免周期中断的NMI watchdog影响xenomai实时性需要关闭NMI watchdog,传递内核参数nmi_watchdog=0
.
GRUB_CMDLINE_LINUX="isolcpus=2,3 xenomai.supported_cpus=0x06 nohz_full=2,3 rcu_nocbs=2,3 irqaffinity=0,1 acpi_irq_nobalance noirqbalance i915.enable_rc6=0 i915.enable_dc=0 i915.disable_power_well=0 i915.enable_execlists=0 i915.powersave=0 nmi_watchdog=0"
nosoftlockup
linux内核参数,禁用 soft-lockup检测器。
GRUB_CMDLINE_LINUX="isolcpus=2,3 xenomai.supported_cpus=0x06 nohz_full=2,3 rcu_nocbs=2,3 irqaffinity=0,1 acpi_irq_nobalance noirqbalance i915.enable_rc6=0 i915.enable_dc=0 i915.disable_power_well=0 i915.enable_execlists=0 i915.powersave=0 nmi_watchdog=0 nosoftlockup"
CPU特性[x86]
intel处理器相关内核参数:
nosmap
nohalt
。告诉内核在空闲时,不要使用省电功能PAL_HALT_LIGHT。 这增加了功耗。但它减少了中断唤醒延迟,这可以提高某些环境下的性能,例如联网服务器或实时系统。mce=ignore_ce
,忽略machine checkerrors (MCE).idle=poll
,不要使用HLT在空闲循环中进行节电,而是轮询以重新安排事件。 这将使CPU消耗更多的功率,但对于在多处理器基准测试中获得稍微更好的性能可能很有用。 它还使使用性能计数器的某些性能分析更加准确。clocksource=tsc tsc=reliable
,指定tsc作为系统clocksource.intel_idle.max_cstate=0
禁用intel_idle并回退到acpi_idle.processor.max_cstate=0 intel.max_cstate=0 processor_idle.max_cstate=0
限制睡眠状态c-state。
GRUB_CMDLINE_LINUX="isolcpus=2,3 xenomai.supported_cpus=0x06 nohz_full=2,3 rcu_nocbs=2,3 irqaffinity=0,1 acpi_irq_nobalance noirqbalance i915.enable_rc6=0 i915.enable_dc=0 i915.disable_power_well=0 i915.enable_execlists=0 nmi_watchdog=0 nosoftlockup processor.max_cstate=0 intel.max_cstate=0 processor_idle.max_cstate=0 intel_idle.max_cstate=0 clocksource=tsc tsc=reliable nmi_watchdog=0 nosoftlockup intel_pstate=disable idle=poll nohalt nosmap mce=ignore_ce"
3.2 内核构建配置
系统构建时,除以上提到的配置外(CONFIG_NO_HZ_FULL = y、CONFIG_RCU_NOCB_CPU=y、RCU_KTHREAD_PRIO=0),其他实时性相关配置如下:
CONFIG_MIGRATION=n、CONFIG_MCORE2=y[x86]、CONFIG_PREEMPT=y、ACPI_PROCESSOR =n[x86]、CONFIG_CPU_FREQ =n、CONFIG_CPU_IDLE =n;
经过以上配置后可以使用latency测试,观察配置前后的变化。关于latency,需要注意的是,测试timer-IRQ的latency时,即用latency -t2
命令来测试时,xenomai默认使用cpu0的timer,上面提到boot CPU(通常是0号CPU)会无条件的从nohz_full=[cpu列表]
列表中剔除,所以latency -t2
测试时你会发现没什么变化,还可能会变差了(最坏情况差不多一致,平均值变大了),另外我们将linux中断affinity全都设置为CPU0处理,这些中断或多或少也会影响timer-IRQ的latency。
三、实时性能测试
笔者对以上各个条件配置前后对比过实时性改善效果,均有不同程度的优化效果,在此就不一一贴出前后对比图了,大家有兴趣可自行测试。
下面直接给出最终的应用空间任务Jitter测试结果,使用的环境如下:
CPU | intel 赛扬 [email protected] |
Kernel | Linux 4.4.200 |
操作系统 | Ubuntu 16.04 |
内存 | 8GB DDR3-1600 双通道 |
存储 | 64GB EMMC |
测试条件:在stress压力下测试,同时一个QT应用程序绘制2维曲线图,QT CPU占用率99%。
stress -c 10 -m 4
测试时间:211:04:55
测试命令:
latency -t0 -p 100 -P 99 -h -g result.txt
测试应用空间程序,优先级99,任务周期100us,测试结果输出到文件result.txt。经过接近10天的测试后,文件result.txt中latency分布结果如下:
# 211:04:55 (periodic user-mode task, 100 us period, priority 99)
# ----lat min|----lat avg|----lat max|-overrun|---msw|
# 0.343| 1.078| 23.110| 0| 0|
# Xenomai version: Xenomai/cobalt v3.1
# Linux 4.4.200-xeno
......
# I-pipe releagese #20 detected
# Cobalt core 3.1 detected
# Compiler: gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.12)
# Build args: --enable-smp --enable-pshared --enable-tls
PKG_CONFIG_PATH=:/usr/xenomai/lib/pkgconfig:/usr/xenomai/lib/pkgconfig0 1
0.5 1599357037
1.5 1621130106
2.5 56618753
3.5 4386985
4.5 3848531
5.5 3556704
6.5 3353649
7.5 3033218
8.5 2560133
9.5 2035075
10.5 1516866
11.5 1038989
12.5 680815
13.5 417124
14.5 224296
15.5 115165
16.5 58075
17.5 27669
18.5 11648
19.5 4648
20.5 1646
21.5 467
22.5 38
23.5 1
其中第一列数据表示latency的值,第二列表示该值与上一个值之间这个范围的latency出现的次数,最小0.343us,平均latency 1.078us,最大23.110us。可见xenomai的实时性还是挺不错的。
以上只是xenomai应用空间任务的实时性表现,如果使用内核空间任务会更好。当然这只能说明操作系统能提供的实时性能,具体的还要看应用程序的设计等。
此外,该测试基于X86平台,X86处理器的实时性与BIOS有很大关系,通常BIOS配置CPU具有更高的吞吐量,例如超线程、电源管理、CPU频率等,毕竟BIOS不是普通开发者能接触到的,如果能让BIOS对CPU针对实时系统配置的话,实时性会更好。