在x86汇编中,有一个锁前缀,它可以应用于指令,使其成为原子指令。这是跨所有内核的原子性吗?通常涉及的延迟是多少?对于常规互斥锁,锁定的是哪条指令?
谢谢。PS:我被教导说,在缺乏这样的指令的系统上,互斥锁仍然可以完成,但它更费力。我想知道还有没有人这样做。
发布于 2011-07-29 10:24:37
在x86上,lock前缀锁定所有核心并允许原子性。为了在没有LOCK的其他系统上实现这一点,可以使用CMPXCHG循环,或者使用带有重试逻辑的紧密循环,尝试将某些内容的值设置为预期值。正如你所看到的,第二种方法在大多数情况下更有害,因为它只是不断地循环,试图设置值(并一直这样做,直到它完成)。对于x86,延迟是最小的,可能范围是停止指令流水线或刷新它,然后自动执行该指令(通常是几个周期),第二种方法不能真正估计,因为它取决于需要自动修改的值的争用程度。对于互斥锁,我认为它是一个必须获取的标志值(检查互斥锁是否未被获取,并持续等待,直到互斥锁被抓取,然后尝试自动更改标志以获取它)。
AFAIK IBM处理器使用第二种方法,因为在linux内核上工作时,我发现原子增量函数使用它(可能它只适用于较老的处理器)。x86平台仍然使用
lock addl ...;我承认,我在内核的这一部分工作已经有一年了,所以在某些方面我可能是错的。
发布于 2018-04-23 17:59:23
在x86程序集中有一个锁前缀,它可以应用于指令,使其成为原子指令。这是跨所有内核的原子性吗?
是。
通常涉及的延迟是多少?
成本因多种因素而异(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时间。这具有竞争条件,并最终成为内核调度程序中的一个原子“检查是否可以获得互斥量,然后更改任务的状态”。
还要注意,这是相当昂贵的,所以大多数实现都试图在用户空间中乐观地做尽可能多的事情;这样,如果没有争用,那么在获取互斥锁时不需要涉及内核;因此,如果没有阻塞等待互斥锁,则内核不会被告知解锁不存在的等待任务。如果互斥锁争用来增加内核不参与的可能性,也有一些变体会旋转一小段时间。
换句话说,获取和释放互斥锁的代码通常不在一个地方--它是两个部分,一个部分在用户空间,另一个部分在内核中。
https://stackoverflow.com/questions/6868007
复制相似问题