8

我正在使用 Win32 API 来停止/启动/检查/更改线程状态。一般来说效果很好。有时它会失败,我试图找出原因。

我有一个线程通过以下方式强制在其他线程上进行上下文切换:

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(十六进制核心)上,它偶尔会出现断点,ECX = 0200h。在我的 Win 7 Intel i7 系统上以同样的方式失败。我会忽略这一点,除非它暗示 EFLAGS 没有被正确存储,正如我所怀疑的那样。

根据我对 Intel(以及 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 位,这似乎无关紧要。

仍然对解释感兴趣,但显然这不是我偶尔的上下文切换崩溃的根源。

4

1 回答 1

1

微软长期以来一直在不真正使用的地方储存一些位。Raymond Chen 给出了很多例子,例如使用非字节对齐的指针的低位。

在这种情况下,Windows 可能需要将其一些线程上下文存储在现有CONTEXT结构中,并决定在EFLAGS. 无论如何,你不能用那个位做任何事情,当你调用SetThreadContext.

于 2014-04-02T15:36:17.193 回答