2 个版本
0.1.1 | 2021年10月15日 |
---|---|
0.1.0 | 2021年10月12日 |
444 在 #concurrency
每月 21 次下载
用于 linux-rtic
47KB
1K SLoC
linux-rtic
实时 Linux 的 RTIC 实现。
工作原理
此 RTIC 实现基于 std::thread
,通过为每个任务优先级组创建一个线程。线程使用 SCHED_FIFO
实时策略初始化。任务优先级与 Linux 优先级一一对应,通常范围在 1-99 之间。
调度
任务调度由 futex-queue 完成,它巧妙地利用 futex 系统调用在单个系统调用上等待即时和计划(定时)任务。不需要定时器线程(和额外的上下文切换)。
资源锁定
原始 cortex-m-rtic 使用栈资源策略(SRP),但在用户空间 Linux 中难以模拟。首先,为每个加锁/解锁设置线程优先级涉及昂贵的系统调用(在树莓派 4 上约为 10us)。其次,设置线程优先级并不能保证低优先级线程不会运行。低优先级线程可能在不同的核心上执行,或者在更高优先级线程挂起时(即 I/O 系统调用)。虽然可以通过备份同步机制(互斥锁)来修复内存安全性问题,但系统调用开销对于实时应用来说太高。
为了解决这个问题,编写了一个 pcp-mutex 库,它实现了原始优先级天花板协议(OPCP)。这允许保留 SRP 的两个重要属性:限制优先级反转和静态防止死锁。这个互斥锁在快速路径上是无锁的。技术细节请参考 pcp-mutex 的 README 文件。
其他说明
在用户空间线程中调度任务较慢,因为上下文切换开销大(在树莓派 4 上约为 10us),已经探索了其他方法。
- 较旧的 linux-rtfm 实现使用了 POSIX 信号。它们比线程上下文切换更快,但是任务仅限于重入(信号安全)函数,这迫使用户必须使用
no_std
。此外,由于信号屏蔽系统调用,资源锁定速度较慢。 - 内核线程仅略微更快,因为大部分开销似乎都在调度器本身。因此,失去用户空间安全和 std 库似乎不值得。
- 硬中断上下文将最接近 cortex-m-rtic 的实现,但 Linux 不支持中断优先级(只有 IRQ 线程有优先级),并且需要对内核进行重大修改。
示例
运行示例需要带有 PREEMPT-RT 补丁的 Linux 内核以及 root 权限,以支持 SCHED_FIFO
。可以通过使用 --no-default-features
编译来移除此要求,但这样所有任务将共享相同的优先级。
构建
cargo build --release --example priority_inversion
运行(需要 sudo 执行 sched_setscheduler
系统调用)
sudo target/release/examples/priority_inversion
单核
sudo taskset -c 1 target/release/examples/priority_inversion
没有实时优先级
cargo run --release --example priority_inversion --no-default-features
使实时更真实的技巧
- 应用
PREEMPT-RT
内核补丁,并通过CONFIG_PREEMPT_RT_FULL
编译内核以减少内核中的不可抢占部分。 - 禁用动态 CPU 频率缩放。可以在内核配置中设置,或者使用
cpufreq-set -g performance
。 - 使用
isolcpus
内核参数在隔离的核心上运行 RTIC。 - 确保外围进程(例如
spi0
)具有实时优先级:sudo chrt -f -p 50 $(pidof spi0)
. - 尝试限制不同优先级任务之间的调度,以减少上下文切换开销。
- 观看 编写 Linux 实时应用程序的清单 - John Ogness, Linutronix GmbH
致谢
这项工作是在特温特大学论文的一部分。
依赖关系
~2.5MB
~55K SLoC