鉴于此,在 ARM Cortex M3 上,我可以:
- 原子地读取一个位
- 原子地设置一个位
- 原子清除一位
我如何将这些组合成互斥体风格的一组操作:
try lock
take lock
release lock
似乎try_lock
ortake_lock
将需要两个不是原子的操作。
我需要更多的控制来完成这个吗?禁用全局中断可以做到这一点,但似乎应该有一种更外科手术的方法。
rwl_TryLock()
如果在调用锁时已经持有锁,则不一定会返回失败(您的编译器应该至少给出关于没有返回值的代码路径的警告)。尝试以下操作:
int rwl_TryLock(volatile uint32_t *lock, int who){
Var_SetBit_BB((uint32_t)lock, who);
if(*lock == (1<<who)){ // check that we have exclusive access
// got the lock!
return 1;
}
// do not have the lock
Var_ResetBit_BB((uint32_t)lock, who); // clear the lock flag
return 0;
}
请注意,以上内容不适用于递归地声明相同的锁(即,如果指定的任务who == 1
已经拥有锁并尝试再次声明它,则上面的代码将无法正常工作),但您的原始代码确实如此好。
此外,可以非常快速地在 Cortex M3 上禁用/启用中断(这是对 NVIC 寄存器的简单更新)。你确定你的系统不能忍受额外的几个中断延迟周期来保持处理锁数据结构的代码简单(这通常意味着更容易纠正)吗?
一些搜索后的信息:
" ARM Cortex-M3 位带 ARM 的微控制器内核提供了另一种实现信号量的方法。对位带别名区域中的变量的写访问会导致对位带区域中的内存位置的原子读-修改-写访问系统总线级别。这如何转换为信号量?位带区域中的变量可以用作信号量的容器。每个客户端“拥有”该容器中的一个位。每当客户端需要声明信号量时,它都会设置自己的通过将 1 写入位带别名区域中的相应位置来位。然后它将读取容器(位带区域)并检查是否没有设置其他位,这意味着客户端已成功声明信号量。如果设置了其他位,客户端将不得不再次清除自己的位,然后重试(可能在等待之后)。"(来源)
这是我的粗略(未经测试)解释:
/*
* Frees a lock.
*
* @note lock must point to a fully aligned 32 bit integer.
* (atomically set to 0)
*
* @returns 1 if successfull
*/
int rwl_FreeLock(volatile uint32_t *lock){
*lock = 0;
return 1; // always successful
}
/*
* Attempts to acquire a lock
* @param who is the client taking the lock
* @lock pointer to the mutex (uint32_t value in memory)
* @note lock must point to a fully aligned 32 bit integer.
* (atomically set to 1 only if set to 0)
*/
int rwl_TryLock(volatile uint32_t *lock, int who){
// initial check of lock
if(*lock == 0){
Var_SetBit_BB((uint32_t)lock, who);
if(*lock == (1<<who)){ // check that we still have exclusive access
// got the lock!
return 1;
} else {
// do not have the lock
Var_ResetBit_BB((uint32_t)lock, who); // clear the lock flag
return 0;
}
}
}
Var_Set_BB / Var_Reset_BB:使用位带设置/清除位。(原子)
然而,它不起作用!!!
位带不适用于这种情况。这只是在设备寄存器文件和内存中设置位的一种非常巧妙的方法。使用 Load Exclusive 和 Store Exclusive 指令来实现您的信号量/互斥量。这是您可以使用的示例文档,它使用这些说明实现信号量,并详细说明了它是如何工作的。
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0439b/CHDDIGAC.html
话虽如此,您可以通过使用位带来减少互斥锁的内存占用......
我从未在 ARM 上使用过位带;我的倾向是对所有此类操作使用 load-exclusive/store-conditional。使用循环加载排除旧值,计算新值,然后使用条件存储将其写回。循环直到条件存储成功(如果不是第一次,它可能会第二次)。