6个版本 (稳定)

1.1.3 2024年8月21日
1.1.2 2023年8月9日
1.1.1 2022年9月13日
1.0.0-alpha.2 2022年7月28日
0.2.1 2021年5月12日

#7嵌入式开发

Download history 94867/week @ 2024-05-01 93143/week @ 2024-05-08 101218/week @ 2024-05-15 106542/week @ 2024-05-22 101162/week @ 2024-05-29 96957/week @ 2024-06-05 125723/week @ 2024-06-12 115674/week @ 2024-06-19 114145/week @ 2024-06-26 107677/week @ 2024-07-03 114525/week @ 2024-07-10 109611/week @ 2024-07-17 96029/week @ 2024-07-24 101122/week @ 2024-07-31 107390/week @ 2024-08-07 116558/week @ 2024-08-14

每月440,382次下载
用于 1,475 个包 (381 直接)

MIT/Apache

33KB
248

critical-section

crates.io crates.io Documentation

该项目由HAL团队开发和维护。

适用于任何地方的临界区!

在编写嵌入式系统软件时,通常使用“临界区”作为控制并发的基本原语。临界区本质上是一个全局互斥锁,一次只能由一个线程获取。这可以用来保护互斥锁后面的数据,在不支持它们的目标上模拟原子操作,等等。

根据执行环境的不同,有各种可能的实现方式。

  • 对于裸机单核,禁用当前(仅有的)核心的中断。
  • 对于裸机多核,禁用当前核心的中断并获取硬件自旋锁以防止其他核心同时进入临界区。
  • 对于使用实时操作系统的裸机,使用库函数来获取临界区,通常称为“调度锁”或“内核锁”。
  • 对于在非特权模式下运行的裸机,通常需要调用某些系统调用。
  • 对于std目标,获取全局std::sync::Mutex

库通常需要使用临界区,但在core中并没有通用的API。这导致库作者为他们的目标硬编码它们,或者最多添加一些cfg来支持几个目标。由于存在许多目标,并且通常无法仅从Rust目标知道需要哪种临界区实现,因此这无法扩展。例如,thumbv7em-none-eabi目标可能是上面的1-4种情况之一。

该包通过提供这个缺失的通用API来解决此问题。

  • 它提供函数acquirereleasewith,库可以直接使用这些函数。
  • 它为任何crate提供了一个实现方式。这允许“目标支持”crate,例如架构crate(cortex-mriscv),RTOS绑定或用于多核芯片的HALs提供正确的实现,以便依赖树中的所有crate都能自动使用它。

no-std二进制文件中的使用。

首先,添加一个依赖于提供临界区实现crate的依赖。如果crate需要,启用critical-section-* Cargo功能。

实现通常由架构支持crate、HAL crate和OS/RTOS绑定提供,包括

  • cortex-m crate通过其critical-section-single-core功能为所有单核Cortex-M微控制器提供实现
  • riscv crate通过其critical-section-single-hart功能为所有单核心RISC-V微控制器提供实现
  • msp430 crate通过其critical-section-single-core功能为所有MSP430微控制器提供实现
  • rp2040-hal crate通过其critical-section-impl功能为RP2040微控制器提供多核安全的临界区
  • avr-device crate通过其critical-section-impl功能为所有AVR微控制器提供实现
  • esp-hal-common crate为ESP32微控制器提供实现,这些实现由ESP HALs使用
  • embassy-rp crate通过其critical-section-impl功能为RP2040微控制器提供多核安全的临界区
  • nrf-softdevice crate通过其critical-section-impl功能提供了一个与nRF软设备固件兼容的临界区

例如,对于单核Cortex-M目标,您可以使用

[dependencies]
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"]}

然后您可以使用critical_section::with()

use core::cell::Cell;
use critical_section::Mutex;

static MY_VALUE: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));

critical_section::with(|cs| {
    // This code runs within a critical section.

    // `cs` is a token that you can use to "prove" that to some API,
    // for example to a `Mutex`:
    MY_VALUE.borrow(cs).set(42);
});

# #[cfg(not(feature = "std"))] // needed for `cargo test --features std`
# mod no_std {
#     struct MyCriticalSection;
#     critical_section::set_impl!(MyCriticalSection);
#     unsafe impl critical_section::Impl for MyCriticalSection {
#         unsafe fn acquire() -> () {}
#         unsafe fn release(token: ()) {}
#     }
# }

std二进制文件中的使用。

critical-section依赖项添加到Cargo.toml中,并启用std功能。这使得critical-section crate本身根据std::sync::Mutex提供实现,因此您不需要添加任何其他依赖项。

[dependencies]
critical-section = { version = "1.1", features = ["std"]}

在库中的使用

如果您正在编写一个旨在跨许多目标可移植的库,只需添加对critical-section的依赖,并像往常一样使用critical_section::free和/或Mutex

不要添加任何提供临界区实现的依赖项。不要启用任何critical-section-* Cargo功能。这必须由最终用户完成,启用他们目标正确的实现。

不要critical-section中启用任何Cargo功能。

no-std库的std测试中的使用。

如果您想在其他无-std库中运行使用std的测试,请在dev-dependencies中仅启用std功能。这样,主目标将使用终端用户二进制选择的无-std实现,而只有测试目标将使用std实现。

[dependencies]
critical-section = "1.1"

[dev-dependencies]
critical-section = { version = "1.1", features = ["std"]}

提供实现

为特定架构、芯片或操作系统添加支持的任务应该提供临界区实现。强烈建议将实现隐藏在功能之后,以便用户在需要时仍然可以使用其他实现(在同一二进制文件中包含两个实现会导致链接失败)。

添加依赖项,并在您的Cargo.toml中添加critical-section-*功能。

[features]
# Enable critical section implementation that does "foo"
critical-section-foo = ["critical-section/restore-state-bool"]

[dependencies]
critical-section = { version = "1.0", optional = true }

然后,提供如下所示的临界区实现

# #[cfg(not(feature = "std"))] // needed for `cargo test --features std`
# mod no_std {
// This is a type alias for the enabled `restore-state-*` feature.
// For example, it is `bool` if you enable `restore-state-bool`.
use critical_section::RawRestoreState;

struct MyCriticalSection;
critical_section::set_impl!(MyCriticalSection);

unsafe impl critical_section::Impl for MyCriticalSection {
    unsafe fn acquire() -> RawRestoreState {
        // TODO
    }

    unsafe fn release(token: RawRestoreState) {
        // TODO
    }
}
# }

故障排除

未定义的引用错误

如果您遇到如下错误

undefined reference to `_critical_section_1_0_acquire'
undefined reference to `_critical_section_1_0_release'

这通常是因为您(或库)在使用critical_section::with时没有提供临界区实现。请确保您依赖于提供实现的crate,并且如果需要,已在该crate中启用critical-section-*功能。请参阅上面的Usage部分。

错误也可能是由依赖但从未使用它而引起的。这可以通过添加一个虚拟的use来解决。

use the_cs_impl_crate as _;

重复符号错误

如果您遇到如下错误

error: symbol `_critical_section_1_0_acquire` is already defined

这是因为您有两个crate正在尝试提供临界区实现。程序中只能有一个实现。

您可以使用cargo tree --format '{p} {f}'来查看所有依赖项及其启用的功能。请确保在整个依赖项树中,恰好只有一个实现。

还要检查同一crate的多个版本。例如,请确保critical-section-single-core功能对于cortex-m 0.7和0.8都没有启用。

为什么不使用泛型?

另一种解决方案是使用CriticalSection特质,并使所有需要获取临界区的代码对它进行泛型处理。这有几个问题

  • 它需要将泛型参数传递给大量的代码,这会很不方便。
  • 通常将Mutex放在static变量中,而static不能泛型。
  • 它将允许在同一程序中混合不同的临界区实现,这将是不安全的。

最低支持的Rust版本(MSRV)

本crate保证在以下Rust版本上编译

  • 如果未启用std功能:稳定版Rust 1.54及更高版本。
  • 如果启用std功能:稳定版Rust 1.63及更高版本。

它可能可以与较旧版本兼容,但在任何新的补丁版本中可能会更改。

有关如何升级MSRV的详细信息,请参阅此处

许可

本作品受以下任一许可协议的许可:

您可自行选择。

贡献

除非您明确声明,否则任何有意提交以包含在本作品中的贡献(根据 Apache-2.0 许可协议定义),将如上双重许可,不附加任何额外条款或条件。

行为准则

对本 crate 的贡献受 Rust 行为准则 的约束,本 crate 的维护者,HAL 团队(HAL team),承诺将介入以维护该行为准则。

无运行时依赖

功能