【发布时间】:2021-10-17 11:15:15
【问题描述】:
我正在以 64 位模式运行的树莓派 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)上运行测试时,锁会起作用。但是,在树莓派上,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 /
【问题讨论】:
-
你的自旋锁的目的是什么,这是很多工作。您是否考虑过使用其他东西或不必担心自旋锁的方法?我知道这不是您要问的问题,但如果我不得不担心自旋锁,我将永远无法完成任何事情。
-
我的旧树莓派只有一个核心。可能更现代的是多核,但你是裸机,所以也许你没有做适当的多任务处理?因为我认为只有一个核心的自旋锁没有意义。
-
但是测试应该还是可以的,因为这里的锁只是按这个顺序反复上锁和解锁;从来没有争吵。
-
@rodrigo:RPi 3B+ 有 4 个内核。
-
你是裸机,没有启用缓存和MMU?所以
ldxrb总是会失败,因为排他锁永远不会被占用。
标签: rust raspberry-pi arm bare-metal spinlock