如果不同处理器中的两个进程试图在同一时间获得锁定会发生什么

时间:2011-10-23 00:54:56

标签: synchronization locking smp multiprocessor

好的,所以我正在阅读有关同步的内容,并且我阅读了各种算法,例如自旋锁,信号量和互斥锁,以避免竞争条件。

然而,当多个进程完全同时访问数据时,这些算法无法阻止SMP中的竞争条件。

例如,假设处理器A中的线程1运行     锁(mutex1);     退出(1000);     解锁(mutex1);

并且处理器B中的线程2运行     锁(mutex1);     存款(1000);     存款(1000);     解锁(mutex1);

当两个线程在同一时间完全运行时,两个线程将同时处于关键部分。

唯一的解决方案(应该是硬件级别)会使每个处理器彼此略微偏离,但它会破坏并行性的目的。

是否有任何硬件级别支持以避免多个处理器尝试在同一时间获取锁定的情况?

(这不是原子性问题,而是精确并行问题,我想知道SMP如何处理它。)

5 个答案:

答案 0 :(得分:12)

互斥体的全部意义在于,即使两个核心同时尝试获取它,其中一个核心将被阻塞,直到另一个核心释放它为止。允许两个内核同时保存该互斥锁的互斥锁将完全被破坏,并且用于其唯一的预期目的。

在硬件的某个地方有一个总线仲裁器,它只允许一个内核控制连接这两个内核的总线。如果任一核心已经拥有将互斥锁保存在私有缓存中的内存,则该核心将获胜。否则,无论谁先获得公共汽车都将获胜。

总线仲裁器可以以多种方式工作,但通常会轮换。因此,如果内核为0,1,2和3,并且内核2具有总线最后一个,则总线将接下来如果它需要核心3,否则核心0如果需要它,否则核心1如果它想要它,否则回到核心2.根据确切涉及哪个总线(无论是两个核心的L2缓存之间还是内存本身之间或其他什么),一些核心可以作为一个单元对抗其他核心组然后进行子竞争特定核心首先获得它。

可能是一个核心已经控制了总线,因此它将彻底赢得胜利。通常情况下,仲裁器允许核心持续保持总线,只要它继续想要将其用于少数交易,以避免浪费的切换,这些切换不允许核心向前推进。

具体细节可能因许多因素而异,包括如何排列核心,哪些核心在哪些状态下锁定其缓存,谁拥有总线最后以及总线仲裁器是否使用时间片,循环,或其他一些机制,等等。但是,任何不能保证只有一个核心能够获得锁定的实现都会被认为是非常糟糕的。

答案 1 :(得分:2)

您可能希望研究内存障碍。

http://en.wikipedia.org/wiki/Memory_barrier

在这种情况下,锁将使用内存屏障,以便多个处理器不能同时访问锁中使用的内部值。

某些体系结构还允许锁定除1之外的所有核心以实现此目的。例如,x86运行一个LOCK前缀,当添加到指令时将锁定该指令期间对内存的访问。 (例如:LOCK ADD EAX,1表示寄存器的原子增量)

不支持LOCK或原子指令的体系结构使用比较和交换或测试和设置/交换。通常它涉及一个小的指令循环,在高级别可能看起来像

while (target != value) target = value;

这可能看起来不会执行多次,但它确保在指令之间,值不会从它下面改变。这种方法的缺点是,如果目标存在较高的争用,那么它可能会消耗比您想要的更多的时钟周期,但它往往发生得足够快,所以它永远不会真正引人注目。

答案 2 :(得分:1)

我强烈推荐Curt Schimmel's UNIX® Systems for Modern Architectures: Symmetric Multiprocessing and Caching for Kernel Programmers。不同的硬件架构提供了不同的低级工具,用于同步对数据的访问,包括一些非常接近 no 帮助的架构。 Schimmel的书提供了甚至可以在这些架构上运行的算法。

我希望我可以轻松找到我的副本以总结内容。

答案 3 :(得分:0)

您可以使用TLS和XCHG等原子指令来阻止这种情况。

如何确保指令的原子性?

您可以在执行指令之前禁用所有中断,然后在指令完成后全部启用它们。这对多核系统没有帮助,因为禁用处理器1中的中断对处理器2没有任何影响。在多核系统上,通过阻止其他CPU访问内存总线(内存屏障)来确保指令的原子性

因此,如果您使用这些说明实现信号量,则SMP上没有任何问题。

使用TSL实现mutex_lock和mutex_unlock:

 mutex_lock:
    TSL REGISTER, MUTEX ; copy mutex to register and sets mutex
    CMP REGISTER, #0    ; compare mutex to zero
    JZE ok              ; if zero return
    CALL thread_yield   ; else: mutex is busy, schedule another thread
    JMP mutex_lock      ; try again later
 ok: RET

 mutex_unlock:
    MOVE MUTEX,#0       ; free mutex
    RET

您可以在此处找到有关TSL的一些信息: http://en.wikipedia.org/wiki/Test-and-set

一本可以帮助您的好书:http://www.amazon.com/Modern-Operating-Systems-Andrew-Tanenbaum/dp/0136006639/ref=sr_1_sc_1?ie=UTF8&qid=1319333818&sr=8-1-spell

答案 4 :(得分:-1)

这是一个经典的死锁问题。我不确定硬件支持,(但我几乎可以肯定这在硬件级别是受支持的)但是,我可以举例说明数据库中死锁问题的解决方案。如果您知道所有依赖关系,您知道应该“杀死”哪个依赖关系,这样给定节点的命令将失败,但系统将打败死锁,其他节点不会失败。我认为同样的方法应该是硬件级别的。