如何在汇编语言级别实现线程同步?

时间:2010-03-03 01:13:05

标签: multithreading assembly concurrency synchronization x86

虽然我熟悉并发编程概念,例如互斥和信号量,但我从未理解它们是如何在汇编语言级别实现的。

我想有一组内存“标志”说:

  • 锁定A由线程1
  • 保存
  • 锁定B由线程3
  • 保留
  • 锁定C未被任何线程持有

但是如何在线程之间同步访问这些标志?像这个天真的例子只会产生竞争条件:

  mov edx, [myThreadId]
wait:
  cmp [lock], 0
  jne wait
  mov [lock], edx
  ; I wanted an exclusive lock but the above 
  ; three instructions are not an atomic operation :(

3 个答案:

答案 0 :(得分:21)

  • 在实践中,这些通常会使用CASLL/SC来实施。 (......在放弃线程的时间片之前旋转一些 - 通常是通过调用切换上下文的内核函数。)
  • 如果您只需要spinlock,维基百科会为您提供一个示例,用于在x86 / x64上以锁定前缀xchg换取CAS。因此从严格意义上讲,制作自旋锁不需要CAS - 但仍需要某种原子性。在这种情况下,它使用原子操作,该操作可以将寄存器写入存储器并以单步返回该存储器槽的先前内容。 (为了澄清一点: lock 前缀断言#LOCK信号,确保当前CPU具有对内存的独占访问权限。在今天的CPU上,它不一定以这种方式执行,而是效果通过使用xchg,我们确保在读取和写入之间不会被抢占,因为指令不会在中途中断。所以如果我们有一个虚构的 lock mov reg0,mem / lock mov mem,reg1 对(我们没有),它们可能会在两个mov之间被抢占。)
  • 在当前的体系结构中,正如评论中所指出的,您最终会使用CPU的原子基元和内存子系统提供的一致性协议。
  • 因此,您不仅要使用这些原语,还要考虑架构保证的缓存/内存一致性。
  • 也可能存在实施细微差别。考虑到例如一个螺旋锁:
    • 而不是天真的实现,你应该使用例如一个TTAS spin-lock with some exponential backoff
    • 在超线程CPU上,您应该发出pause指令作为您正在旋转的提示 - 这样您运行的核心可以在此期间做一些有用的事情
    • 你应该在一段时间后放弃旋转和控制其他线程
    • 等...
  • 这仍然是用户模式 ​​- 如果您正在编写内核,您可能还可以使用其他一些工具(因为您是调度线程并处理/启用/禁用中断的工具)。

答案 1 :(得分:10)

x86架构长期以来有一条名为xchg的指令,它将寄存器的内容与内存位置进行交换。 xchg一直是原子的。

还有一个lock前缀可以应用于任何一条指令,使该指令成为原子。在有多处理器系统之前,所有这一切确实是为了防止在锁定指令中间传递中断。 (xchg被隐式锁定)。

本文提供了一些使用xchg实现自旋锁的示例代码 http://en.wikipedia.org/wiki/Spinlock

当开始构建多CPU和更高版本的多核系统时,需要更复杂的系统来确保锁和xchg同步所有内存子系统,包括所有处理器上的l1缓存。大约在这个时候,对锁定和无锁算法的新研究表明原子CompareAndSet是一个更灵活的原语,所以更现代的CPU有这个作为指令。

附录:在评论 andras 中提供了一个“尘土飞扬”的指令列表,其中包含lock前缀。 http://pdos.csail.mit.edu/6.828/2007/readings/i386/LOCK.htm

答案 2 :(得分:2)

我喜欢将线程同步视为自下而上的处理器和操作系统提供了更复杂的构造

在处理器级别,您有CAS和LL / SC,它允许您在单个原子操作中执行测试和存储...您还有其他处理器构造,允许您禁用和启用中断(但是它们被认为是危险......在某些情况下,你别无选择,只能使用它们)

操作系统提供了在每次线程使用其时间片时可能发生的任务之间的上下文切换的能力......或者由于更多的原因而发生的事情(我会发现)

然后有更高级别的构造,如互斥体,它使用处理器提供的这些原始机制(认为旋转互斥体)......它将持续等待条件变为真,并以原子方式检查该条件

然后这些旋转互斥体可以使用OS提供的功能(上下文切换和系统调用,如yield,将控件放弃到另一个线程)并为我们提供互斥量

这些构造进一步被更高级别的构造使用,如条件变量(可以跟踪多少线程正在等待互斥锁,以及当互斥锁变为可用时首先允许哪个线程)

这些结构可以进一步用于提供更复杂的同步结构......例如:信号量等