オペレーティングシステムは、優先順位、プロセッサアフィニティなどのさまざまな情報に基づいてスレッドをスケジューリングしています。OSが別のスレッドに実行のチャンスを与えることを決定すると、Context switch (Wikipedia)と呼ばれます。コンテキスト切り替え中、オペレーティングシステムは現在のスレッドのレジスタを保存し、新しいスレッドのレジスタを復元します。
内部的には、オペレーティングシステムはすべての情報を維持する必要があります。あなたは簡単に1000のスレッドを持つことができるので、OSはメモリのどこかにすべてのレジスタの1000倍を持たなければなりません。
ユーザーモードデバッガを安全に使用し、カーネル構造を確認できます。あなたがWindows上にいるので、私はの一部であるwindbgを使用します。
プログラムを起動するには(ノートパッドは常に良い候補です)、WinDbg(F6)を添付してください。
まずは、マイクロソフトから正しい情報を取得してみましょう:
0:000> .symfix
0:000> .reload /f
これらのコマンドは、我々は正しいシンボル(のPDB)を持っていることを確認します。
次は、(それがカーネルスケジュールするので、ないスレッドのユーザーモード部分で)カーネルスレッドを見てみましょう:
0:000> dt nt!_KTHREAD
+0x000 Header : _DISPATCHER_HEADER
+0x010 CycleTime : Uint8B
+0x018 HighCycleTime : Uint4B
+0x020 QuantumTarget : Uint8B
+0x028 InitialStack : Ptr32 Void
[...]
+0x1b8 WaitPrcb : Ptr32 _KPRCB
[...]
+0x1f4 ThreadCounters : Ptr32 _KTHREAD_COUNTERS
+0x1f8 XStateSave : Ptr32 _XSTATE_SAVE
私たちが見ることができるように、スレッドを維持するための情報がかなりあります大きい(0x1f8 + 4または508バイト)。あなたはWikipediaの記事を読んでいる場合
、あなたが見つけた:これは通常、プロセス制御ブロック(PCB)またはswitchframeと呼ばれるデータ構造に格納されて
。
これは、オフセット1b8の_KPRCB
構造体です。のは、その1を見てみましょう:私たちは、コンテキストの切り替えを考える
0:000> dt nt!_KPRCB
ntdll!_KPRCB
+0x000 MinorVersion : Uint2B
+0x002 MajorVersion : Uint2B
+0x004 CurrentThread : Ptr32 _KTHREAD
+0x008 NextThread : Ptr32 _KTHREAD
[...]
+0x3658 Context : Ptr32 _CONTEXT
+0x365c ContextFlags : Uint4B
+0x3660 ExtendedState : Ptr32 _XSAVE_AREA
、の_CONTEXT
が見て正しいことであると仮定しましょう。
0:000> dt nt!_CONTEXT
+0x000 ContextFlags : Uint4B
+0x004 Dr0 : Uint4B
+0x008 Dr1 : Uint4B
+0x00c Dr2 : Uint4B
+0x010 Dr3 : Uint4B
+0x014 Dr6 : Uint4B
+0x018 Dr7 : Uint4B
+0x01c FloatSave : _FLOATING_SAVE_AREA
+0x08c SegGs : Uint4B
+0x090 SegFs : Uint4B
+0x094 SegEs : Uint4B
+0x098 SegDs : Uint4B
+0x09c Edi : Uint4B
+0x0a0 Esi : Uint4B
+0x0a4 Ebx : Uint4B
+0x0a8 Edx : Uint4B
+0x0ac Ecx : Uint4B
+0x0b0 Eax : Uint4B
+0x0b4 Ebp : Uint4B
+0x0b8 Eip : Uint4B
+0x0bc SegCs : Uint4B
+0x0c0 EFlags : Uint4B
+0x0c4 Esp : Uint4B
+0x0c8 SegSs : Uint4B
+0x0cc ExtendedRegisters : [512] UChar
はい、レジスタは次のとおりです。
そして、何を知っていますか?私は32ビットプロセスに接続されているようですので、おそらく別の結果が得られます。とにかく、もう一度試してみて、あなたが得られます。
0:000> dt nt!_CONTEXT
+0x000 P1Home : Uint8B
+0x008 P2Home : Uint8B
+0x010 P3Home : Uint8B
+0x018 P4Home : Uint8B
+0x020 P5Home : Uint8B
+0x028 P6Home : Uint8B
+0x030 ContextFlags : Uint4B
[...]
+0x078 Rax : Uint8B
+0x080 Rcx : Uint8B
+0x088 Rdx : Uint8B
+0x090 Rbx : Uint8B
+0x098 Rsp : Uint8B
+0x0a0 Rbp : Uint8B
+0x0a8 Rsi : Uint8B
+0x0b0 Rdi : Uint8B
+0x0b8 R8 : Uint8B
+0x0c0 R9 : Uint8B
[...]
+0x280 Xmm14 : _M128A
+0x290 Xmm15 : _M128A
+0x300 VectorRegister : [26] _M128A
+0x4a0 VectorControl : Uint8B
+0x4a8 DebugControl : Uint8B
+0x4b0 LastBranchToRip : Uint8B
+0x4b8 LastBranchFromRip : Uint8B
+0x4c0 LastExceptionToRip : Uint8B
+0x4c8 LastExceptionFromRip : Uint8B
概要:それはレジスタを維持しているカーネルは、必要に応じてタイプ_CONTEXT
のように多くの「オブジェクト」を作成します。コンテキストスイッチが発生するたびに、カーネルは現在のレジスタを保存し、他のレジスタを復元します。
デバッグ時、スレッドは中断されているため、CPU上では実行されません。デバッガと対話できる必要があるため、CPUを停止することもできません。 CPUがデバッガの命令を実行しています。ただし、デバッガは_KTHREAD
の情報をあなたに提供します。
これはすべて非常に簡略化されていますが、今は十分かもしれません。ソフトウェアやハードウェアのコンテキストスイッチ(read at OSWiki)などがあります。他のユーザーモードレジスタなどを復元する前に、カーネルがどのようにレジスタを取得するのかは確かに興味深いですが、それはあまりにも多くのSOの投稿です。
テストプログラムがブレークポイントに達すると、CPU、デバッガ、およびその他のプログラムが実行されています。デバッガには、ブレークポイント時のレジスタの値が表示されます。また、テストプログラムが継続しているとき、ステップを超えたとき、または再び実行するときに残った値に復帰します。 – rcgldr
プログラムがブレークポイントをスローすると、タイムスライスのレジスタやその他のリソース値を見ることができます。これはあなたのものであなたのプログラムに属する地域の値を見ることができることを意味します。それはあなたのOSによってリソースを共有した結果です。実際にBPを要求するたびに、デバッガはあなたのコードに 'INT 3'命令を入れます。おそらく、この命令のOSは、あなたのデバッガに属するRAMの特別な領域にあなたの必要な値のコピーを作成し、次のプロセスに切り替えます。[リンク](http://x86.renejeschke.de/html/file_module_x86_id_142。 html) –
他のプログラム(デバッガ自体を含む)は他のプロセスで実行され、OSによって "プロセス/スレッド"メカニズムが提供され、OSはCPU割り込み/ context_switchingを使用して現在実行中のマシンコードを中断し、他のプロセス/スレッドの状態がまったく異なっていて、「スリープ状態」であり、他のプロセスが短い間実行されています...単一のプロセスがCPUのシングルコアで実行されます。 「マルチタスキング」は、異なるプロセスの状態を常に中断/セーブ/リロードすることによって実現されます。 – Ped7g