汇编:xadd指令需要锁吗?

时间:2015-05-08 18:42:17

标签: assembly x86 smp

我正在阅读allan cruse code

的smphello.s代码

在接下来的部分中,他试图为每个处理器设置堆栈段。

关键是他在xadd的描述中使用了xadd而不使用锁定前缀,如here中所示。可能有一个锁定前缀。

这是一个错误还是没关系?为什么?

# setup an exclusive stack-area for this processor
mov  $0x1000, %ax   # paragraphs in segment
xadd %ax, newSS     # 'atomic' xchg-and-add
mov  %ax, %ss       # segment-address in SS
xor  %esp, %esp     # top-of-stack into ESP

4 个答案:

答案 0 :(得分:4)

没有xadd

lock是原子wrt。在该内核上中断,但不中断。在其他内核(或DMA)上运行的代码。就像xchg以外的所有其他内存目标RMW一样。请参见this answer,其中涉及cmpxchg的相同问题。

如果此代码在多个内核上同时运行,则2个或更多内核可以读取newSS的相同值,从而有效地失去增量并将相同的ss:esp分配给两个内核。或者一个存储可能被恰好是顺序的其他内核延迟跨多个xadd进行存储,从而将计数器“倒回”到后来加载看到的某个先前值。或任何问题的组合。 Can num++ be atomic for 'int num'?

假设newSS对齐,则负载和存储分别是原子的,但不是形成原子RMW。

如果同时唤醒多个内核(是否可以广播IPI?),这似乎是一个真正的问题。如果不是这样,则每个xadd都有可能在下一个内核到达此代码之前完成。 (包括将存储提交到L1d缓存的存储;在全局范围内可见。)但这只是一种“工作要做”的行为,除非核心唤醒功能在唤醒另一个核心之前先等待一个核心的回音。

如果要与有关原子增量的注释匹配,肯定需要lock xadd是原子wrt。如果线程从不并发运行,则只能通过单个内核上的上下文切换来进行中断。 (例如,主线程与信号处理程序或同一内核上的中断处理程序之间的原子性)。但是,由于其标题为smphello.s,因此单处​​理器假设似乎不太可能。

答案 1 :(得分:4)

仅为这些理论论证提供一些经验证据:

这是一个测试用例,其中多个线程使用xadd来增加共享计数器。在具有4核的i7-8565U上,它输出

unlocked: counter = 1633267, expected 4000000
locked: counter = 4000000, expected 4000000

清楚地表明没有xadd的{​​{1}}不是原子的。

代码:

lock

答案 2 :(得分:3)

经过另一次思考,我想起了另一种情况。

如果xadd的微代码实现是这样的:

temp = ax + newSS
newSS = ax 
ax = temp ; the last 2 are actual xchg

我们在这种情况下遇到了问题:

假设newSS在两个线程之间共享。

第0号主题(t0ax等于 5 )加载并newSS添加ax并将其放入temp register

假设此时我们有一个上下文切换。然后t1使用ax等于 5 尝试加载newSS并将其添加到ax并将结果放入temp register }。然后上下文切换回t0 ... 两个堆栈段寄存器都指向相同的地址。

显然我们这里有问题。除非微码实现是这样的:

tmp register = ax
xchg ax, newSS
ax = ax + tmpRegister

以任何其他方式变量newSS被多次读取或读取和写入不同的指令,我们需要锁定。

答案 3 :(得分:3)

竞争条件不在“新SS”上。 正如教学解释所暗示的那样,

xadd %ax, newSS表示:

temp = ax + newSS
newSS = ax 
ax = temp ; the last 2 are actual xchg

因此竞争条件是在ax寄存器上。 但是如果我们仔细思考,我们知道每个线程都有自己的寄存器文件或上下文。所以没有问题。 (寄存器值不会在上下文切换时从线程的最后知识改变)

因此,在此示例中无需为xadd锁定。