Xenomai
项目是一个开源的 RTOS-to-Linux 可移植框架,遵循 Creative Commons BY-SA 3.0 和 GPLv2 许可证,有两种形式:
Linux
的共核/实时扩展(RTE),代号为 Cobalt
。Linux
(包括 PREEMPT-RT
)的库,代号为 Mercury
。它旨在 3.0 分支中既能作为共核使用,也能在 PREEMPT_RT
上运行。Xenomai
项目将一个名为 Cobalt
核心的实时内核合并到了 Linux
内核中,并在内核空间中共存。Cobalt
核心中的中断和线程优先级比 Linux
内核中的中断和线程更高。由于 Cobalt
核心执行的指令较 Linux
内核少,因此可以减少调用路径中的不必要延迟和历史负担。
使用这种实时目标设计,Xenomai
修补的Linux
内核可以实现良好的实时多线程性能。
两阶段中断管道是实现 Cobalt
实时框架的底层机制。
Cobalt
补丁 Linux
内核在硬件中断上占据主导地位,这些中断原本属于 Linux
内核。Cobalt
将首先处理它感兴趣的中断,然后将其他中断路由到 Linux
内核。前者称为 head stage,后者称为 root stage:
[Head] 阶段的优先级高于 [Root] 阶段,通过压缩硬件和软件的处理时间提供最短的响应时间。
在 Linux
上,taskset
命令允许你更改进程的 CPU 亲和性。通常,它与内核命令行中确定的 CPU 隔离一起使用。以下示例脚本演示了将实时进程的亲和性更改为核心 1 的操作:
cpu="1"
taskset -c $cpu latency -c1 -t0 -p 100 -P 99 -h -g result.txt
如果作为systemd服务作为系统守护进程,可以参考下面的设置:
[Unit]
Description=cobalt latency service
Wants=network-online.target
[Service]
CPUAffinity=1
ExecStart=/usr/local/bin/latency -c1 -t0 -p 100 -P 99 -h -g result.txt
Type=exec
[Install]
WantedBy=multi-user.target
Alias=latency.service
Cobalt
核心的线程并不是完全与 Linux
内核的线程隔离。相反,它重用了普通的 kthread
并添加了特殊功能;kthread
可以在 Cobalt
的实时上下文(out-of-band 实时域)和普通的 Linux
内核上下文(in-band 非实时域)之间切换。其优势在于,当处于实时域上下文时,线程可以利用 Linux
内核的基础设施。在典型场景中,Cobalt
线程会以普通 kthread
的形式启动,调用 Linux
内核的 API 进行准备工作,然后切换到实时域上下文,作为 Cobalt
线程执行实时工作。其劣势在于,处于实时域上下文时,Cobalt
线程很容易因为误调用 Linux
内核 API 而迁移到非实时上下文。在这种情况下,问题很难被发现;开发者通常误认为他们的线程正在 Cobalt
核心下运行,直到检查 ftrace
输出或任务超出其截止期限时才注意到问题。
基于 Cobalt
的用户空间应用程序可以在抢占式调度和通用分时(SCHED_OTHER)调度策略之间影射同一线程:
非实时域:此模式下可以访问 Linux GPOS 服务和 Linux [ROOT] 域设备驱动程序(例如,可以使用 ps -x 或 top 等 Linux 命令)。
实时域:此模式下可以访问所有 Xenomai RTOS 服务和 RTDM [HEAD] 域设备驱动程序。
可以通过以下命令查看进程状态:
cat /proc/xenomai/sched/stat
当使用 libcobalt.so
链接时,通过 pthread_create()
和 pthread_setschedparam()
设置的 Linux pthread,任何对标准(S) POSIX 调度策略的更改,例如 SCHED_FIFO
和 SCHED_RR
,都会通过一种称为影射 (shadowing) 的机制,跳转到 Cobalt
任务。
此外,Xenomai/Cobalt 提供了补充的调度策略(X):
SCHED_RR
或 SCHED_FIFO
任务) 干扰。Scheduling Policies | 基础Linux | 基础Linux + PREEMPT_RT | 基础Linux + Xenomai/COBALT |
---|---|---|---|
SCHED_TP, SCHED_BATCH, SCHED_IDLE | S | S | S |
SCHED_FIFO, SCHED_RR | N | N | N |
SCHED_FIFO | N | P | P |
SCHED_TP, SCHED_SPORADIC, and SCHED_QUOTA | - | - | X |
注释:
MetaOS 支持使用X86 硬件定时器,以基于高优先级 实时任务中断创建时间间隔:
timerfd_handler
POSIX 定时器 API 从用户空间调用clock_nanosleep()
通过高精度定时器硬件卸载实现线程精确唤醒Linux
用户空间文件系统接口允许报告 Cobalt
定时器信息:通过下面的命令查看Cobalt计时器信息:
cat /proc/xenomai/timer/coreclk
在实时系统开发中,调度算法的选择和应用对系统的性能和实时性至关重要。Xenomai
的 Cobalt
实时内核为开发者提供了多种调度策略,以满足不同应用场景的需求。将深入探讨 Cobalt
的补充调度算法,包括 SCHED_QUOTA
、SCHED_TP
、SCHED_WEAK
和 SCHED_SPORADIC
,并详细解释它们的工作原理、应用场景以及如何在实际开发中有效地利用这些策略。
在实时系统中,任务的调度需要考虑到多种因素,如任务的优先级、执行时间、资源使用等。传统的调度策略,如 SCHED_FIFO
和 SCHED_RR
,在某些场景下可能无法满足复杂的调度需求。为此,Cobalt
提供了多种补充调度策略,帮助开发者更灵活地管理系统中的线程和资源。
SCHED_QUOTA
是一种基于配额(Quota)的调度策略,它通过限制线程在固定的时间周期内的 CPU 使用量,来实现对线程组的资源控制。在该策略下,线程被分配到不同的线程组(Thread Group),每个组在全局配额周期内被分配一定的运行时间配额(以百分比表示)。在组内,所有线程遵循 SCHED_FIFO
策略。
全局配额周期(Global Quota Period):这是一个固定的时间周期,例如 1 秒(默认),整个系统的配额管理都基于这个周期。
线程组配额:每个线程组被分配一定比例的运行时间,例如 35%、25% 等。
线程执行:
SCHED_QUOTA
策略的线程获得 CPU 资源时,其消耗的时间会被计入所属线程组的配额。假设系统中有 5 个线程组,每个组的配额和全局周期如下:
SCHED_QUOTA 调度策略示例图
在每个全局周期内,各线程组可以在其配额内运行线程。当组内所有线程的总运行时间达到配额后,该组将被暂停,等待下一个周期。
运行时间预算和峰值配额
运行时间预算:在每个全局周期开始时,线程组获得其完整的配额。如果该组在当前周期未完全消耗其配额,剩余的预算会被累积到下一个周期,直到达到定义的峰值配额(Peak Quota)。
峰值配额:这是线程组可累积的最大配额。当累积的预算超过峰值配额时,超出的部分会在后续多个周期内逐步消耗。
应用场景
SCHED_TP 策略实现了一种称为**时间分区(Temporal Partitioning)**的机制,它通过将 CPU 时间划分为固定的时间窗口,确保不同线程组在时间上不发生重叠地执行。
主时间帧(Global Time Frame):一个固定的、重复的全局时间周期。
次要帧(Secondary Frames):主时间帧被划分为多个次要帧(时间窗口),每个次要帧具有固定的持续时间和相对于主时间帧的偏移量。
分区(Partitions):每个次要帧分配给一个特定的线程组(分区)。在次要帧的时间窗口内,只有该分区内的线程被允许执行。
线程执行:
假设主时间帧为 100 毫秒,被划分为 5 个次要帧:
在每个次要帧内,只有对应分区内的线程被调度执行。这样可以确保不同分区的线程在时间上严格隔离,防止相互干扰。
SCHED_TP 调度策略示例图
应用场景
SCHED_WEAK
策略用于实现弱调度(Weak Scheduling),其主要目的是让线程能够与实时线程进行同步,但不与实时线程竞争 CPU 资源。
工作原理
SCHED_WEAK
类的线程具有较低的优先级,不会抢占实时线程。SCHED_WEAK
线程在 Cobalt
系统调用返回后,会自动离开实时域,回到非实时模式。SCHED_WEAK
线程可以使用 Cobalt
提供的同步原语(如互斥锁、条件变量)与实时线程进行同步。应用场景
SCHED_SPORADIC
零星调度策略通常用于对给定时间段内的线程执行时间提供上限。在零星调度下,线程的优先级可以在前台(正常优先级)与后台(低优先级)之间动态振荡。与 FIFO 调度一样,使用零星调度的线程会继续执行,直到它被更高优先级的线程阻塞或抢占。与自适应调度一样,使用零星调度的线程的优先级会降低,但使用零星调度可以更精确地控制线程的行为。
初始预算(C):线程在被降级为低优先级 (L) 之前被允许以正常优先级 (N) 执行的时间量。
低优先级(L):线程将降级到的优先级。线程在后台时以这个较低的优先级 (L) 执行,在前台时以正常优先级 (N) 运行。
补充期(T):线程被允许消耗其执行预算的时间段。为了安排补充操作,POSIX
实现还使用此值作为线程变为 READY 的时间偏移量。
待处理补充的最大数量:此值限制可以进行的补充操作的数量,从而限制不规则调度策略消耗的系统开销。
执行过程:
示例图解:
SCHED_SPORADIC 调度策略示例图
时间轴:
这样线程将继续在其两个优先级之间振荡,以一种受控的、可预测的方式为系统中的非周期性事件提供服务。
应用场景