在多核x86系统上,是否使用LOCK'd指令实现了互斥?

时间:2011-07-29 02:19:38

标签: assembly x86

在x86程序集中,有一个LOCK前缀可以应用于使其成为原子的指令。所有内核都具有原子性吗?通常的延误是什么?对于常规互斥锁,它被锁定了什么指令?

感谢。 PS:我被教导说,在缺乏这种指令的系统上,仍然可以完成互斥,但它更加费力。我想知道是否有人这样做了。

2 个答案:

答案 0 :(得分:9)

在x86上,锁定前缀锁定所有内核并允许原子性。为了在没有LOCK的其他系统上实现这一点,使用CMPXCHG循环或使用重试逻辑的紧密循环尝试将某事物的值设置为期望值。正如你所看到的,第二种方式在大多数情况下更有害,因为它只是不断地循环尝试设置值(并且一直这样做直到完成)。对于x86,延迟是最小的,可能包括暂停指令流水线或刷新它然后原子地执行该指令(通常是几个周期),第二种方法无法真正估计,因为它取决于有多少争用。需要原子修改的值。对于互斥锁,我认为它是必须获取的标志值(检查互斥锁是否未被获取并持续等待互斥锁启动然后尝试以原子方式更改标记以获取它)。

AFAIK IBM处理器在使用linux内核时使用第二种方法我发现原子增量函数使用它(可能它只适用于较旧的处理器)。 x86平台仍然使用

lock addl ...;

我承认,虽然我在内核的这一部分工作已经有一年了,所以在某些方面我可能错了。

答案 1 :(得分:2)

  

在x86程序集中,有一个LOCK前缀可以应用于使其成为原子的指令。这是所有核心的原子性吗?

  

通常涉及的延误是什么?

成本取决于多种因素(CPU型号,CPU速度,总线速度,RAM速度,当时数据实际所在的位置以及尝试使用总线的其他因素)。对于非常老的CPU(8086,80186,80286,80386),没有缓存,LOCK锁定总线以确保没有其他任何东西可以干扰。这不会比没有LOCK的相同指令花费更多,除了在指令持续时间内尝试使用的所有其他内容都必须等待(它减慢了其他所有内容,而不是减慢代码速度)本身)。

对于所有现代CPU(我猜“Pentium III或更高版本”),它被改为依赖MESI缓存一致性协议。具体来说,高速缓存行被带入高速缓存并变为“独占”状态,然后仅使用本地高速缓存完成指令而不锁定总线。这个成本取决于数据的位置 - 例如如果它已经在同一个CPU的L1数据缓存中(而不是在其他CPU的缓存中),则LOCK可能不会花费任何成本。但是,(由于多CPU代码的性质),高速缓存行通常位于不同CPU的高速缓存中,需要从一个高速缓存传输到另一个高速缓存和/或在其他CPU的高速缓存中无效(通过称为“读取”的内容)所有权“在MESI缓存一致性协议中”因此它的成本更高(但仍然没有锁定总线那么多)。

  

对于常规互斥锁,它被锁定的指令是什么?

多年来,我看到互斥体实现了大约20种不同的方式。在所有不同的实现中,没有一条指令是相同的。

请注意,当您无法获取互斥锁时,内核会被告知在释放互斥锁之前不会给您的任务任何CPU时间;然后当释放互斥锁时,需要告知内核该任务可以再次消耗CPU时间。这具有竞争条件,最终成为原子“检查是否可以获取互斥锁,然后在内核的调度程序中深入更改任务的状态。”

另请注意,这是相当昂贵的,因此大多数实现都试图在用户空间中尽可能乐观地做;因此,如果没有争用,则在获取互斥锁时不需要涉及内核;因此,如果没有阻止等待互斥锁的内容,则不会告诉内核解除阻塞不存在的等待任务。如果互斥体争用增加内核不参与的可能性,也会有一些变化会短暂旋转。

换句话说;获取和释放互斥锁的代码通常甚至不在一个地方 - 它是两部分,一部分在用户空间而另一部分在内核中。