以下64位NASM代码使用锁cmpxchg以内核顺序获取每个内核,执行一些代码,然后使用xchg重置内核编号变量,以便下一个内核可以执行该代码。每个核心的核心编号存储在rbx中-四个核心分别编号为0、8、16和24。变量[spin_lock_core]从零开始,每个核心完成后,它在最后一行将核心编号更新为8。 xchg [spin_lock_core],rax。
Spin_lock:
xor rax,rax
lock cmpxchg [spin_lock_core],rbx
jnz Spin_lock
; Test
mov rbp,extra_test_array
mov [rbp+rbx],rbx
; Execute some code before looping out
mov rax,1234
mov rdx,23435
add rax,rbx
mov rcx,rax
;jmp label_899
mov rax,rbx
add rax,8
xchg [spin_lock_core],rax
但是在代码到达xchg [spin_lock_core]之前,先破坏第一个内核循环出程序(jmp label_899),这将导致其他线程冻结,因为它们将等待[spin_lock_core]变量被更新,从而永远不会发生。但是,相反,所有四个内核都写入输出数组extra_test_array,该数组在程序退出时显示在终端上。换句话说,这无法停止内核,直到更新内核编号为止。
下面是完整的,最少的代码(在这种情况下,最少为NASM)。该代码是为共享库编写的,如果它获得输入数组,则可以重现(如所编写的,输入数组是int还是float都没有关系):
; Header Section
[BITS 64]
[default rel]
global Main_Entry_fn
extern pthread_create, pthread_join, pthread_exit, pthread_self, sched_getcpu
global FreeMem_fn
extern malloc, realloc, free
extern sprintf
section .data align=16
X_ctr: dq 0
data_master_ptr: dq 0
initial_dynamic_length: dq 0
XMM_Stack: dq 0, 0, 0, 0, 0, 0, 0
ThreadID: dq 0
X_ptr: dq 0
X_length: dq 0
X: dq 0
collect_ptr: dq 0
collect_length: dq 0
collect_ctr: dq 0
even_squares_list_ptrs: dq 0, 0, 0, 0
even_squares_list_ctr: dq 0
even_squares_list_length: dq 0
Number_Of_Cores: dq 32
pthread_attr_t: dq 0
pthread_arg: dq 0
Join_Ret_Val: dq 0
tcounter: dq 0
sched_getcpu_array: times 4 dq 0
ThreadIDLocked: dq 0
spin_lock_core: dq 0
extra_test_array: dq 0
; __________
section .text
Init_Cores_fn:
; _____
; Create Threads
label_0:
mov rdi,ThreadID ; ThreadCount
mov rsi,pthread_attr_t ; Thread Attributes
mov rdx,Test_fn ; Function Pointer
mov rcx,pthread_arg
call pthread_create wrt ..plt
mov rdi,[ThreadID] ; id to wait on
mov rsi,Join_Ret_Val ; return value
call pthread_join wrt ..plt
mov rax,[tcounter]
add rax,8
mov [tcounter],rax
mov rbx,[Number_Of_Cores]
cmp rax,rbx
jl label_0
; _____
jmp label_900 ; All threads return here, and exit
; ______________________________________
Test_fn:
; Get the core number
call sched_getcpu wrt ..plt
mov rbx,8 ; multiply by 8
mul rbx
push rax
pop rax
mov rbx,rax
push rax
Spin_lock:
lock cmpxchg [spin_lock_core],rbx
jnz Spin_lock
; Test
mov rbp,extra_test_array
mov [rbp+rbx],rbx
; Execute some code before looping out
mov rax,1234
mov rdx,23435
add rax,rbx
mov rcx,rax
jmp label_899
mov rax,rbx
add rax,8
xchg [spin_lock_core],rax
;__________
label_899:
pop rax
ret
; __________
label_900:
mov rdi,extra_test_array ;audit_array
mov rax,rdi
ret
;__________
;Free the memory
FreeMem_fn:
;The pointer is passed back in rcx (of course)
sub rsp,40
call free wrt ..plt
add rsp,40
ret
; __________
; Main Entry
Main_Entry_fn:
push rdi
push rbp
push rbx
push r15
xor r15,r15
push r14
xor r14,r14
push r13
xor r13,r13
push r12
xor r12,r12
push r11
xor r11,r11
push r10
xor r10,r10
push r9
xor r9,r9
push r8
xor r8,r8
movsd [XMM_Stack+0],xmm13
movsd [XMM_Stack+8],xmm12
movsd [XMM_Stack+16],xmm11
movsd [XMM_Stack+24],xmm15
movsd [XMM_Stack+32],xmm14
movsd [XMM_Stack+40],xmm10
mov [X_ptr],rdi
mov [data_master_ptr],rsi
; Now assign lengths
lea rdi,[data_master_ptr]
mov rbp,[rdi]
xor rcx,rcx
movsd xmm0,qword[rbp+rcx]
cvttsd2si rax,xmm0
mov [X_length],rax
add rcx,8
; __________
; Write variables to assigned registers
mov r15,0
lea rdi,[rel collect_ptr]
mov r14,qword[rdi]
mov r13,[collect_ctr]
mov r12,[collect_length]
lea rdi,[rel X_ptr]
mov r11,qword[rdi]
mov r10,[X_length]
; __________
call Init_Cores_fn
movsd xmm10,[XMM_Stack+0]
movsd xmm14,[XMM_Stack+8]
movsd xmm15,[XMM_Stack+16]
movsd xmm11,[XMM_Stack+24]
movsd xmm12,[XMM_Stack+32]
movsd xmm13,[XMM_Stack+40]
pop r8
pop r9
pop r10
pop r11
pop r12
pop r13
pop r14
pop r15
pop rbx
pop rbp
pop rdi
ret
[lock cmpxchg]指令应失败,直到[spin_lock_core]变量被更新为止,但它不会这样做。
感谢您在理解为什么锁cmpxchg不能阻止核心零之后的核心在此代码区域内触发的任何帮助。
更新:其他研究表明,在Spin_lock:部分的顶部需要xor rax,rax。当我插入该行时,其内容如下:
Spin_lock:
xor rax,rax
lock cmpxchg [spin_lock_core],rbx
jnz Spin_lock
如预期,该更改将冻结。但是,当我删除行jmp label_899时,它仍然冻结,但不应这样做。
编辑122219:
[基于昨天对这个问题的评论,我修改了自旋锁代码,以(1)消除原子操作,以便使用更快的mov和cmp指令,(2)为每个内核分配一个唯一的内存位置,(3)分开内存位置> 256字节,以避免在同一高速缓存行上存储。
上一个内核完成后,每个内核的内存位置将更改为1。每个内核完成后,它将自己的内存位置设置回0。
如果自旋锁之前所有其他内核都退出循环,则代码将成功执行内核0。当我让所有四个内核都通过自旋锁运行时,程序再次挂起。
我已经验证了在上一个内核完成后,每个单独的内存位置都设置为1。
这里是更新的自旋锁部分:
section .data spin_lock_core: times 140 dq 0 spin_lock_core_offsets: dq 0,264,528,792 section .text ; Calculate the offset to spin_lock_core mov rbp,spin_lock_core mov rdi,spin_lock_core_offsets mov rax,[rdi+rbx] add rbp,rax ; ________ Spin_lock: pause cmp byte[rbp],1 jnz Spin_lock xor rax,rax mov [rbp],rax ; Set current memory location to zero ; Execute some code before looping out mov rax,1234 mov rdx,23435 add rax,rdx mov rcx,rax ; Loop out if this is the last core mov rax,rbx add rax,8 cmp rax,[Number_Of_Cores] jge label_899 ; Set next core to 1 by adding 264 to the base address add rbp,264 mov rax,1 mov [rbp],rax
为什么此代码仍然挂起?
以下64位NASM代码使用锁cmpxchg以内核顺序获取每个内核,执行一些代码,然后使用xchg重置内核编号变量,以便下一个内核可以执行该代码。核心...
我认为您根本不应该使用cmpxchg。试试这个:
我解决了这个自旋锁问题,但是在彼得·科德斯(Peter Cordes)在下面发表评论后,我发现这是不正确的。我不会删除此答案,因为我希望它能导致解决方案。