我有一个线程通过以下方式强制其他线程上的上下文切换:
thread stop fetch processor state into windows context block read thread registers from windows context block to my own context block write thread registers from another context block into windows context block restart thread
这非常好用……但……很少,上下文切换似乎失败了.
(症状:我的多线程系统在执行奇怪的寄存器内容的奇怪地方时高高飘扬).
上下文控制通过以下方式完成:
if ((suspend_count=SuspendThread(WindowsThreadHandle))<0) { printf("TimeSlicer Suspend Thread failure"); ... } ... Context.ContextFlags = (CONTEXT_INTEGER | CONTEXT_CONTROL | CONTEXT_FLOATING_POINT); if (!GetThreadContext(WindowsThreadHandle,&Context)) { printf("Context fetch failure"); ... } call ContextSwap(&Context); // does the context swap if (ResumeThread(WindowsThreadHandle)<0) { printf("Thread resume failure"); ... }
没有任何打印语句被执行.我得出结论,Windows认为上下文操作都是可靠的.
哦,是的,我确实知道什么时候停止的线程没有计算[例如,在系统功能中]并且不会尝试停止/上下文切换它.我知道这一点,因为每个执行除计算之外的任何事情的线程都设置了一个特定于线程的“请勿触摸我”标志,而它正在进行非计算. (设备驱动程序程序员会将此视为“中断禁用”指令的等效项).
所以,我想知道上下文块的内容的可靠性.
我在上下文块中提取的各种寄存器值上添加了各种健全性测试;你实际上可以确定ESP是正常的(在TIB中定义的堆栈区域的范围内),PC在我期望的程序中或在系统调用中等等.这里没有惊喜.
我决定检查条件代码位(EFLAGS)是否被正确读出;如果这是错误的,它将导致切换任务在其状态为时采取“错误的分支”
恢复.因此,我添加了以下代码,以验证所声称的EFLAGS寄存器包含的内容根据英特尔参考手册(http://en.wikipedia.org/wiki/FLAGS_register)仅显示为EFLAGS.
mov eax,Context.EFlags[ebx] ; ebx points to Windows Context block mov ecx,eax ; check that we seem to have flag bits and ecx,0FFFEF32Ah ; where we expect constant flag bits to be cmp ecx,000000202h ; expected state of constant flag bits je @f breakpoint ; trap if unexpected flag bit status @@:
在我的Win 7 AMD Phenom II X6 1090T(hex core)上,
它会偶尔陷入断点,ECX = 0200h.在我的Win 7 Intel i7系统上失败.我会忽略这一点,
除了它暗示EFLAGS没有正确存储,我怀疑.
根据我对英特尔(以及AMD)参考手册的阅读,第1位保留,值始终为“1”.不是我在这里看到的.
显然,MS通过在线程停止上执行复杂的操作来填充上下文块.我希望他们准确地存储状态.该位未正确存储.
如果他们没有正确存储这个位,他们还存储了什么?
为什么这个位的值有时/应该为零的任何解释?
编辑:我的代码转储寄存器和堆栈捕获断点.
堆栈区域包含上下文块作为局部变量.
EAX和上下文块中EFLAGS的适当偏移量中的堆栈中的值都包含值0244h.所以上下文块中的值确实是错误的.
EDIT2:我将掩码和比较值更改为
and ecx,0FFFEF328h ; was FFEF32Ah where we expect flag bits to be cmp ecx,000000200h
这似乎可靠运行,没有任何抱怨.显然,Win7没有正确执行eflags的第1位,看起来并不重要.
仍然对解释感兴趣,但显然这不是我偶尔的上下文切换崩溃的来源.
在这种情况下,Windows可能需要将其某些线程上下文存储在现有的CONTEXT结构中,并决定在EFLAGS中使用其他未使用的位.无论如何你都无法做任何事情,当你调用SetThreadContext时,Windows会得到这一点.