私はおそらくこの質問にかなり遅れて立ち入りましたが、それから、他のプログラマーに役立つかもしれません. まず - 理論。
現代のオペレーティング システムはメモリを仮想化し、そのためにシステム メモリ領域内に一連のページ ポインタを保持します。各ページは固定サイズ (通常は 4K) であり、プログラムがメモリをシークすると、割り当てられたメモリ アドレスがメモリ ページ ポインタを使用して仮想化されます。これは、前世代のプロセッサの「セグメント」レジスタの動作に似ています。
スケジューラが別のプロセスを実行することを決定した場合、以前のプロセスをメモリに保持する場合と保持しない場合があります。それがメモリに保持されている場合、スケジューラはレジスタのスナップショット全体を保存するだけです (現在、YMM レジスタを含む - コンテキスト全体を保存する単一の命令がないため、このビットは以前は複雑な問題でした: XSAVE を参照してください)。 )、これは固定形式です (Intel SW マニュアルで入手可能)。これは、使用されていたメモリ ページに関する情報とともに、スケジューラ自体のメモリ空間に格納されます。
ただし、スケジューラがスリープ状態になろうとしている現在のプロセス コンテキストをハードディスクに「ダンプ」する必要がある場合、通常、この状況は、ウェイクアップ中のプロセスが異常な量のメモリを必要とする場合に発生し、スケジューラはメモリ ページを書き込みます。ディスクブロック内のファイル (ページファイルと呼ばれる - メモリの予約領域 - ページファイルは実メモリのサイズと等しくなければならないという「おばあさんの知恵」の源でもあります) と、スケジューラはメモリページポインタアドレスをページファイルのオフセットとして保存します。起動すると、スケジューラはページファイルからオフセット アドレスを読み取り、実メモリを割り当ててメモリ ページ ポインタを設定し、ディスク ブロックからコンテンツをロードします。
ここで、特定の質問に答えるには: 1. 相対アドレス指定のみを使用する必要がありますか、それとも絶対アドレス指定を使用できますか?
と。いずれかを使用できます-メモリページポインターがそのアドレスを目に見えない形式で相対化するため、絶対的であると認識しているものはすべて相対的です。オペレーティングシステム自体のカーネルを除いて、絶対的なメモリアドレスはどこにもありません (io デバイスメモリを含む)。これをテストするには、任意の .EXE プログラムをアンアセンブルして、エントリ ポイントが常に CALL 0010 であることを確認します。これは、各スレッドが異なる "0010" を取得して実行を開始することを明確に意味します。
- スレッドはどのように生命を得るのですか? また、未使用のスライスを放棄した場合はどうなりますか?
答え。スレッドは通常、スライスを取得します - 現代のシステムは通常の標準として 20 ミリ秒です - しかし、これは、処理するハードウェア割り込みが多くないサーバーの特別な目的のコンパイルで変更されることがあります - プロセスキューでの位置の順序で。スレッドは通常、関数 sleep() を呼び出すことによってそのスライスを放棄します。これは、タイム スライスの残りの部分を放棄するための正式な (そして非常に優れた方法) です。非同期読み取りまたは割り込みアクションを実装するほとんどのライブラリは、sleep() を内部的に呼び出しますが、多くの場合、最上位プログラムも sleep() を呼び出します。たとえば、タイム ギャップを作成するためです。スリープを呼び出すと、プロセス コンテキストが確実に変更されます。実際には、CPU には NOP を使用してスリープする自由が与えられていません。
もう 1 つの方法は、IO が完了するのを待つことであり、これは別の方法で処理されます。IO プロセスを要求するプログラムは、そのタイム スライスを譲ります。プロセス スケジューラは、このスレッドに「WAITING FOR AN IO」状態であることを示すフラグを立てます。このスレッドには、意図した IO まで、プロセッサによってタイム スライスが与えられません。完了するか、タイムアウトします。この機能は、sleep_until_IO() のようなインターフェイスを明示的に記述する必要がないため、プログラマーに役立ちます。
これにより、探索をさらに進めることができると信じてください。