#thread #linux #proc-macro

linux-rtic-macros

linux-rtic crate 的过程宏

2 个版本

0.1.1 2021年10月15日
0.1.0 2021年10月12日

444#concurrency

每月 21 次下载
用于 linux-rtic

Apache-2.0 OR MIT

47KB
1K SLoC

linux-rtic

License Cargo Documentation

实时 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