| 简单地讲,竞争冒险可以用如下的例子来说明,当两个线程都需要对某一临界资源进行操作时: Thread A:
 while(0 == semaphore)
 {
 /* 问题点A */
 semaphore = 1;
 }
 /* 后续对共享资源的处理 */
 
 Thread B:
 while(0 == semaphore)
 {
 /* 问题点B */
 semaphore = 1;
 }
 /* 后续对共享资源的处理 */
 假设线程A运行到问题点A,此时正好发生了线程的切换,线程B获得了执行权,此时semaphore的值还是为0,于是线程B认为自己获得对共享资源的控制,而之后一旦线程A获得执行权,又会认为自己也获得了执行权,于是就出现了对共享资源的竞争冒险。
 
 而解决这种竞争冒险的问题,一种办法是应用图灵奖获得者Edsger Dijkstra设计的软件信号量,一种办法是使用体系结构提供的类似于test and increase这样的原子操作,还有一种办法稍微傻点(当初我刚做arm的时候不知道swp这条指令),于是就用了关中断-获取变量-修改变量-开中断这种方法,在单核的cpu中也能实现类似于信号量的操作,但是在多核系统中完全失去作用了。
 在ARMv6之前信号量的实现由SWP和SWPB指令来实现,而在ARMv6及之后的kernel中则推荐使用LDREX和STREX命令对来实现信号量。
 
 
 ARM体系结构提供的原子操作就是SWP(B),SWP的语法如下:
 SWP rd,rm,[rs]
 这条语句会将总线锁住,完成如下两步操作:
 1、        rd = [rs]
 2、        [rs] = rm
 也许你会问,这怎么实现信号量操作啊?请看如下示例代码:
 sem_lock;
 swp rd,rm,[rs]
 cmp rd,#0x0
 jne sem_lock
 
 比如说锁打开的时候为0,关闭为1,那么如果在锁打开时,执行sem_lock就会使rd = 0,而锁的值变为1,实现了关锁。因为swp是原子操作,所以不用担心执行过程中锁的值放生改变。
 而如果此时锁为关闭状态,那么执行sem_lock就会是rd的值为1,软件此时就应该认为获取锁失败,不断循环以等待其持有者释放。
 在新的架构中使用LDREX和STREX对来实现信号量,关于这两个指令比较复杂,需要更多的时间去讨论和理解,这两个指令的一个好处是从架构级别对共享资源进行保护,而不是以前的那种需要软件协商的方式,在swp这种模式中,只要有一个线程不遵守规则,那么一切保护都将成为空谈,而LDREX/STREX方式中,可以由硬件机制作出一定程度的保证。
 
 |