我一直在搞乱一些x86程序集,因为它出现在我的许多课程中。特别是,我想将比较和交换(CAS)作为用户函数公开。这是为了实现我自己的锁。
我在Intel CPU上使用Linux 2.6.31和GCC 4.1.1。
我有以下内容:
// int cmpxchg(int *dest, int expected, int update)
.globl cmpxchg
cmpxchg:
pushl %ebp
movl %esp, %ebp
// edx holds dest
movl 8(%ebp), %edx
// eax holds expected value
movl 12(%ebp), %eax
// ecx holds the new value
movl 16(%ebp), %ecx
// cmpxchg dest_addr, exp_value
// compare to %eax is implicit
lock cmpxchgl %edx, %ecx
leave
ret
这是在* .s文件中,我用我的驱动程序编译。当我包括该行
lock cmpxchgl %edx, %ecx
并执行,我收到“非法指令”错误。 当我用
替换该行时 cmpxchgl %edx, %ecx
我的代码似乎运行良好。
首先,lock
是否必要?我不确定cmpxchgl
是否是自然原子的,所以我使用lock
来确定。作为用户地计划,我甚至可以使用lock
吗?
由于
=============================================== =================
我的最终代码(对于那些可能在将来徘徊的人):
// int cmpxchg(int *dest, int expected, int update)
.globl cmpxchg
cmpxchg:
pushl %ebp
movl %esp, %ebp
// edx holds dest, use eDx for Destination ;-)
movl 8(%ebp), %edx
// eax holds expected value implicitly
movl 12(%ebp), %eax
// cmpxchg dest_add, src_value
lock cmpxchgl %edx, 16(%ebp)
leave
ret
答案 0 :(得分:7)
cmpxchgl %edx, (%ecx)
除非目标是内存操作数,否则此操作没有意义,但该指令允许寄存器目标。如果指令使用寄存器模式,CPU将发生故障。
我尝试过,你的代码使用内存操作数。我不知道你是否意识到这一点,但这个序列(带寄存器目的地)有一个流行的名字:“f00fc7c8 bug”或“the F00F bug”。在Pentium时代,这是一个“HCF”(停止和着火)或“杀手捅”指令,因为它会产生一个例外,由于总线被锁定而无法服务,并且它可以从用户调用模式。我认为可能存在操作系统级别的软件解决方法。
答案 1 :(得分:3)
Ross的答案已经说明了大部分内容,但我会尝试澄清一些事情。
LOCK
前缀。唯一的例外是XCHG
(非CMPXCHG
)指令,默认情况下会被锁定,正如asveikau指出的那样。LOCK
是完全合法的。CMPXCHG
与注册目标操作数一起使用是完全合法的。也就是说,将LOCK CMPXCHG
与一起使用与注册目的地操作数一起 是合法的。引用the IA-32 manual的第2A卷(我的副本中的第3-538页):
LOCK前缀只能作为以下指令的前缀,并且只能作为目标操作数是存储器操作数的那些形式的指令:ADD,ADC,AND,BTC,BTR,BTS,CMPXCHG,CMPXCHG8B,DEC,INC ,NEG,NOT,OR,SBB,SUB,XOR,XADD和XCHG。
答案 2 :(得分:1)
你的程序在这里编译得很好(GNU为2.20)(我把它粘贴到test.s并运行作为-o test.o test.s )
关于锁,英特尔的文档说:
该指令可与a一起使用 LOCK前缀允许指令 以原子方式执行。简化 处理器总线的接口, 目标操作数接收到 写循环而不考虑 比较结果。该 目标操作数将被写回 比较失败;否则, 源操作数写入 目的地。 (处理器永远不会 不会产生锁定的读取 产生一个锁定的写。)
答案 3 :(得分:1)
好奇,这个最终代码仍然正确吗?从我所看到的,你正在进行相反的比较,即你正在比较指针的值(即指针所指的实际地址)与用作更新的整数...目标设置为临时int用作更新值。换句话说,而不是:
lock cmpxchgl %edx, 16(%ebp)
我认为你会想要这样的东西:
//move the update value into ecx register
movl 0x16(%ebp), %ecx
//do the comparison between the value at the address pointed to by edx and eax,
//and if they are the same, copy ecx into the address being pointed to by edx
lock cmpxchgl %ecx, (%edx)
原始代码是否真的按计划工作(不仅仅是编译),如果没有,您最终是否重新组织代码,使其看起来更像上面的那样?
答案 4 :(得分:0)
这似乎不太可能是问题的根源,但official documentation也表明该指令不早于486架构支持。这是在实模式下发生的,保护模式覆盖吗?