我正在以 64 位模式运行的 Raspberry Pi 3 上进行 Rust 裸机编程。我已经实现了一个自旋锁,如下所示:
use core::{sync::atomic::{AtomicBool, Ordering}, cell::UnsafeCell, ops::{Deref, DerefMut}};
pub struct SpinMutex<T> {
lock: AtomicBool,
data: UnsafeCell<T>
}
impl<T> SpinMutex<T> {
#[allow(dead_code)]
pub const fn new(data: T) -> Self {
Self {
lock: AtomicBool::new(false),
data: UnsafeCell::new(data)
}
}
pub fn lock(&self) -> SpinMutexGuard<T> {
while self.lock.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed).is_err() {}
SpinMutexGuard {
lock: &self.lock,
data: unsafe { &mut *self.data.get() }
}
}
}
unsafe impl<T> Sync for SpinMutex<T> {}
pub struct SpinMutexGuard<'a, T> {
lock: &'a AtomicBool,
data: &'a mut T
}
impl<'a, T> Deref for SpinMutexGuard<'a, T> {
type Target = T;
fn deref(&self) -> &T {
self.data
}
}
impl<'a, T> DerefMut for SpinMutexGuard<'a, T> {
fn deref_mut(&mut self) -> &mut T {
self.data
}
}
impl<'a, T> Drop for SpinMutexGuard<'a, T> {
/// The dropping of the MutexGuard will release the lock it was created from.
fn drop(&mut self) {
self.lock.store(false, Ordering::Release);
}
}
#[cfg(test)]
mod tests {
use super::{SpinMutex};
#[test]
fn test_spin_mutex() {
let state = SpinMutex::new(0);
assert_eq!(*state.lock().data, 0);
*state.lock().data = 9;
assert_eq!(*state.lock().data, 9);
}
}
当我在本地计算机(64 位 Windows)上运行测试时,锁可以工作。然而,在 Raspberry Pi 上,
lock
方法陷入无限循环并且永远不会返回。出现这种情况有什么原因吗?
这是 Rust 如何在禁用内联的情况下编译
compare_exchange_weak
:
80ba8: 9100400a add x10, x0, #0x10
80bac: 085f7d48 ldxrb w8, [x10]
80bb0: 34000068 cbz w8, 80bbc
80bb4: d5033f5f clrex
80bb8: 14000004 b 80bc8
80bbc: 52800029 mov w9, #0x1 // #1
80bc0: 080b7d49 stxrb w11, w9, [x10]
80bc4: 3400004b cbz w11, 80bcc
80bc8: 2a1f03e9 mov w9, wzr
80bcc: 7100011f cmp w8, #0x0
80bd0: 52000120 eor w0, w9, #0x1
80bd4: 1a9f07e1 cset w1, ne /
这里有一个非详尽的条件列表,我确定(我已经以两种方式测试了它们)需要满足这些条件才能使原子在
EL1
(ARMv8) 中的 RPi 4 aarch64 上工作。
这可能与 RPi 3 (ARMv7) 非常相似。
SCTLR_EL1
位 [0] 设置为 0b1
)SCTLR_EL1
位 [2] 设置为 0b1
)MAIR
标记为正常的可缓存内存(我使用过 0xff
- 我不确定哪些位是多余的。原子,但我不认为有有充分理由使用其他任何东西来保持正常记忆)。ARM8 自旋锁测试存储独占操作是否成功,否则它们会继续旋转。如果 CPU 未处于正确状态,从另一个项目复制的代码可能会永远旋转。从这个意义上说,上面 Blazej Michalik 的回答和 artless-noise-bye-due2AI 的评论都是开门见山的。在满足 Blazej 的三个条件之前,我无法检测到成功的 STXR 操作,即所有争夺存储独占自旋锁的核心必须启用其缓存和 mmu,并且自旋锁本身必须位于正常的可缓存内存中。我很惊讶 ARM8 上的独占锁定需要正常的可缓存内存(而不是未缓存内存)。我在 Pi3b+、Pi4 和 Pi5 上使用 aarch64 EL3,所有三个平台的行为方式相同。