Win32 API を使用して、スレッドの状態を停止/開始/検査/変更しています。一般的にはかなりうまく機能します。時々失敗するので、原因を突き止めようとしています。
次の方法で、他のスレッドでコンテキスト スイッチを強制しているスレッドが 1 つあります。
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");
...
}
print ステートメントは実行されません。Windows は、コンテキスト操作がすべて確実に行われたと考えていると結論付けています。
ああ、そうです、停止中のスレッドが計算中でないとき (たとえば、システム関数内) を知っていて、それを停止/コンテキスト切り替えしようとしません。コンピューティング以外のことを行う各スレッドは、コンピューティング以外のことをしている間、スレッド固有の「私に触れないでください」フラグを設定するため、私はこれを知っています。(デバイス ドライバー プログラマーは、これを「割り込み無効化」命令と同等のものとして認識します)。
そこで、コンテキストブロックの内容の信頼性について疑問に思いました。コンテキスト ブロックから引き出されたさまざまなレジスタ値にさまざまなサニティ テストを追加しました。実際には、ESP が (TIB で定義されたスタック領域の境界内で) OK である、PC が期待するプログラム内またはシステム コール内にあるなどと判断できます。ここで驚くことはありません。
条件コード ビット (EFLAGS) が正しく読み取られていることを確認することにしました。これが間違っていると、状態が復元されたときに、切り替えられたタスクが「間違った分岐」を取る原因になります。そこで、次のコードを追加して、Intel リファレンス マニュアル ( http://en.wikipedia.org/wiki/FLAGS_register )によると、EFLAGS レジスターと称するものに 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 コア) では、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 を正しく処理しておらず、問題ではないようです。
まだ説明に興味がありますが、明らかに、これは私の時折のコンテキストスイッチのクラッシュの原因ではありません。