【发布时间】:2019-03-03 18:06:20
【问题描述】:
我在 Windows 的 NASM(64 位)中有代码,可以在四核 Windows x86-64 机器上同时运行四个线程(每个线程分配给一个单独的核心)。
线程是在循环中创建的。创建线程后,它调用 WaitForMultipleObjects 来协调线程。要调用的函数是 Test_Function(参见下面的代码)。
每个线程(核心)跨一个大数组执行 Test_Function。第一个内核从数据元素 0 开始,第二个内核从 1 开始,第三个内核从 2 开始,第四个内核从 3 开始,每个内核递增 4(例如,0、4、8、12)。
在 Test_Function 中,我创建了一个小型测试程序,将其中一个输入数据值写入与其起始字节对应的位置,以验证我是否已成功创建四个线程并且它们返回正确的数据。
每个线程都应该写入stride值(32),但测试显示四个字段是随机填写的,有些字段显示为零。如果我多次重复测试,我发现哪些字段的值为 32(其他字段始终显示为 0)并不一致。这可能是 WaitForMultipleObjects 的副作用,但我在文档中没有看到任何东西来证实这一点。
此外,WaitForMultipleObjects 等待 CreateThread 返回的 ThreadHandles;当我检查 ThreadHandles 数组时,它总是显示如下:268444374、32、1652、1584。只有第一个元素看起来像句柄的大小,其他元素看起来不像句柄值。
一种可能是堆栈上传递的两个参数可能不在正确的位置:
mov rax,0
mov [rsp+40],rax ; use default creation flags
mov rax,[ThreadCount]
mov [rsp+32],rax ; ThreadID
根据文档,ThreadCount 应该是一个指针。当我将行更改为 mov rax,ThreadCount (指针值)时,程序崩溃。当我将其更改为:
mov rax,0
mov [rsp+32],rax ; use default creation flags
mov rax,ThreadCount
mov [rsp+40],rax ; ThreadID
现在它可靠地处理第一个线程,但不是线程 2-4。
所以底线是线程正在被创建,但它们随机执行,有些线程根本没有执行,没有特定的顺序。当我更改 CreateThread 参数(如上所示)时,第一个线程执行,但不是线程 2-4。
这是显示相关部分的测试代码。如果需要一个可重现的例子,我可以准备一个。
感谢您的任何想法。
Init_Cores_fn:
; EACH OF THE CORES CALLS Test_Function AND EXECUTES THE WHOLE PROGRAM.
; WE PASS THE STARTING BYTE (0, 8, 16, 24) AND THE "STRIDE" = NUMBER OF CORES.
; ON RETURN, WE SYNCHRONIZE ANY DATA. ON ENTRY TO EACH CORE, SET THE REGISTERS
; Populate the ThreadInfo array with vars to pass
; ThreadInfo: length, startbyte, stride, vars into registers on entry to each core
mov rdi,ThreadInfo
mov rax,ThreadInfoLength
mov [rdi],rax
mov rax,[stride]
mov [rdi+16],rax ; 8 x number of cores (32 in this example)
; Register Vars
mov [rdi+24],r15
mov [rdi+32],r14
mov [rdi+40],r13
mov [rdi+48],r12
mov [rdi+56],r10
mov rbp,rsp ; preserve caller's stack frame
sub rsp,56 ; Shadow space
; _____
label_0:
mov rdi,ThreadInfo
mov rax,[FirstByte]
mov [rdi+8],rax ; 0, 8, 16, or 24
; _____
; Create Threads
mov rcx,0 ; lpThreadAttributes (Security Attributes)
mov rdx,0 ; dwStackSize
mov r8,Test_Function ; lpStartAddress (function pointer)
mov r9,ThreadInfo ; lpParameter (array of data passed to each core)
mov rax,0
mov [rsp+40],rax ; use default creation flags
mov rax,[ThreadCount]
mov [rsp+32],rax ; ThreadID
call CreateThread
; Move the handle into ThreadHandles array (returned in rax)
mov rdi,ThreadHandles
mov rcx,[FirstByte]
mov [rdi+rcx],rax
mov rax,[FirstByte]
add rax,8
mov [FirstByte],rax
mov rax,[ThreadCount]
add rax,1
mov [ThreadCount],rax
mov rbx,4
cmp rax,rbx
jl label_0
; _____
; Wait
mov rcx,rax ; number of handles
mov rdx,ThreadHandles ; pointer to handles array
mov r8,1 ; wait for all threads to complete
mov r9,1000 ; milliseconds to wait
call WaitForMultipleObjects
; _____
;[ Code HERE to do cleanup if needed after the four threads finish ]
mov rsp,rbp
jmp label_900
; __________________
; The function for all threads to call
Test_Function:
; Populate registers
mov rdi,rcx
mov rax,[rdi]
mov r15,[rdi+24]
mov rax,[rdi+8] ; start byte
mov r13,[rdi+40]
mov r12,[rdi+48]
mov r10,[rdi+56]
xor r11,r11
xor r9,r9
pxor xmm15,xmm15
pxor xmm15,xmm14
pxor xmm15,xmm13
; Now test it - BUT the first thread does not write data
mov rcx,[rdi+8] ; start byte
mov rax,[rdi+16] ; stride
cvtsi2sd xmm0,rax
movsd [r15+rcx],xmm0
ret
【问题讨论】:
-
第 1 步:学习使用 ASM 调试器。第 2 步:也许将某些部分重写为 C?
-
(1) 我还没有为 Windows 中的 NASM 找到一个好的可视源代码调试器,(2) 调试器不太可能在调用 Windows API 函数时发现问题,( 3)我不希望它在 C 中。
-
您对每个线程使用相同的
ThreadInfo,因此它们会相互竞争以及与主线程竞争。我强烈建议您首先学习高级语言的线程。尝试同时学习线程和汇编语言是不切实际的。 -
如果你想成功,你必须弄清楚如何使用调试器来单步调试这段代码。
标签: windows multithreading x86-64 nasm