7

協調マルチタスク システムの概念と、それがシングル スレッド アプリケーションでどのように機能するかを正確に理解しようとしています。

私の理解では、これは「各タスク内のプログラマーが定義したポイントで自発的に他のタスクに制御を譲ることによって、複数のタスクが実行されるマルチタスクの形式」です。

では、タスクのリストがあり、1 つのタスクが実行中の場合、実行を別のタスクに渡すことをどのように判断するのでしょうか? また、実行を前のタスクに戻す場合、以前の場所からどのように再開するのでしょうか?

マルチスレッドアプリケーションなしでこれをどのように達成できるか理解できないので、これは少し混乱します。

どんなアドバイスもとても役に立ちます:)

ありがとう

4

4 に答える 4

7

単一のプロセス (または実行のスレッド) が協調マルチタスクを使用する特定のシナリオでは、Windows のファイバーや POSIX setcontext ファミリーの関数などを使用できます。ここではファイバーという用語を使用します。

基本的に、あるファイバーが作業のチャンクの実行を終了し、自発的に他のファイバーの実行を許可したい場合 (したがって「協調」という用語)、他のファイバーのコンテキストに手動で切り替えるか、より一般的には何らかの yield() を実行します。 scheduler() 呼び出しがスケジューラのコンテキストにジャンプすると、スケジューラは実行する新しいファイバーを見つけて、そのファイバーのコンテキストに切り替えます。

ここでのコンテキストとは何を意味するのでしょうか? 基本的にスタックとレジスタ。スタックについて魔法は何もありません。それは、スタック ポインタがたまたま指し示すメモリのブロックにすぎません。プログラムカウンターについても魔法はなく、次に実行する命令を指すだけです。コンテキストを切り替えると、現在のレジスタがどこかに保存され、スタック ポインタが別のメモリ チャンクに変更され、プログラム カウンタが別の命令ストリームに更新され、そのコンテキストの保存されたレジスタが CPU にコピーされ、ジャンプが実行されます。バム、あなたは今、別のスタックで別の命令を実行しています。多くの場合、コンテキスト スイッチ コードは、現在のスタックを変更しないか、変更を元に戻す方法で呼び出されるアセンブリで記述されます。どちらの場合も、スタックまたはレジスタにトレースを残さないため、コードの実行が再開されたときに、何が起こったかわかりません。(繰り返しになりますが、テーマ: メソッド呼び出しはレジスタをいじったり、引数をスタックにプッシュしたり、スタック ポインターを移動したりすると想定していますが、それは単なる C 呼び出し規則です。メソッド呼び出しは、それ自体の痕跡をスタックに残します)。

各スタックは分離されているため、一見ランダムなメソッド呼び出しの連続チェーンが最終的にスタックをオーバーフローすることはありません (これは、相互に連続して呼び出しを行う標準 C メソッドを使用して単純にこのスキームを実装しようとした場合の結果である可能性があります)。これは、各ファイバーが作業中のステート マシンを保持し、定期的に呼び出し側のディスパッチャーのメソッドに戻るステート マシンを使用して手動で実装できますが、実際のファイバー/コルーチンのサポートが広く利用可能であるのに、なぜ面倒なのでしょうか?

また、協調マルチタスキングは、プロセス、保護されたメモリ、アドレス空間などと直交していることも覚えておいてください。Mac OS 9 または Windows 3.x を目撃してください。彼らは、個別のプロセスのアイデアを支持しました。しかし、譲ると、コンテキストが OS コンテキストに変更され、OS スケジューラが実行できるようになり、別のプロセスに切り替える可能性がありました。理論的には、協調マルチタスキングを使用しながら、完全に保護された仮想メモリ OS を使用できます。これらのシステムでは、誤ったプロセスが中断されない場合、OS スケジューラが実行されなかったため、システム内の他のすべてのプロセスがフリーズしていました。**

次の自然な質問は、何かをプリエンプティブにするものです...答えは、現在実行中のタスクを停止し、現在のタスクが解放されるかどうかに関係なく、OS が CPU で割り込みタイマーをスケジュールして、OS スケジューラのコンテキストに戻ることです。 CPU を使用するかどうかに関係なく、CPU を「プリエンプト」します。OS が CPU 特権レベルを使用している場合、(カーネルが構成された) タイマーは下位レベルの (ユーザー モード) コードではキャンセルできませんが、理論的には、OS がそのような保護を使用していない場合、誤ったタスクが割り込みタイマーをマスクしたりキャンセルしたりする可能性があります。 CPUを乗っ取ります。タイマーの外部でスケジューラを呼び出すことができる IO 呼び出しのような他のシナリオがいくつかあります。スケジューラは、他のプロセスの優先度が高くないと判断し、スイッチなしで同じプロセスに制御を戻す場合があります...そして実際には、ほとんどの OS はそうしません。

** 特定の時間内に yield が呼び出されない場合、タイマーを起動しない理由を尋ねるかもしれません。その答えは、マルチスレッド同期にあります。協調システムでは、物事が既知の良好な状態にある場合にのみ譲歩するため、わざわざロックを取得したり、再入を心配したりする必要はありません。この神話上のタイマーが起動した場合、中断されたプログラムの状態が破損している可能性があります。これを処理するためにプログラムを作成する必要がある場合は、おめでとうございます...これで、中途半端なプリエンプティブ マルチタスク システムが完成しました。ちょうどそれを正しく行うかもしれません!とにかく物事を変更する場合は、スレッドや保護されたメモリなどを追加することもできます。これは、主要な OS の歴史です。

于 2013-12-31T18:44:40.310 に答える
3

協調的マルチタスキングの背後にある基本的な考え方は信頼です。つまり、各サブタスクは、プロセッサ時間の他のタスクを枯渇させないように、タイムリーに独自の制御を放棄します。これが、協調型マルチタスキング システムのタスクを徹底的にテストし、場合によっては使用を認定する必要がある理由です。

私は専門家ではありませんが、協調タスクはステート マシンとして実装できると思います。この場合、制御をタスクに渡すと、タスクが何らかの進行を遂げるのに必要な最小限の時間だけ実行されます。たとえば、ファイル リーダーがファイルの次の数バイトを読み取る場合、パーサーがドキュメントの次の行を解析する場合、またはセンサー コントローラーが単一の読み取りを行ってから、協調スケジューラに制御を戻す場合があります。タスクの完了。

各タスクは、従来のブロッキング関数やスレッドのようにスタック フレーム (関数レベル) ではなく、ヒープ (オブジェクト レベル) に内部状態を保持する必要があります。

ハードウェア タイマーに依存してコンテキスト スイッチをトリガーする従来のマルチタスク処理とは異なり、協調型マルチタスク処理は、実行時間の長い各タスクの各ステップが許容可能な短い時間で終了することが保証されるように記述されたコードに依存します。 .

于 2013-03-30T15:17:56.210 に答える
2

タスクは、ディスパッチャーを呼び出す明示的な待機一時停止、または譲歩操作を実行します。IO が完了するのを待機する操作や、負荷の高い計算で明示的に降伏する操作が異なる場合があります。アプリケーション タスクのメイン ループでは、ビジー ポーリングの代わりに *wait_for_event* 呼び出しを行うことができます。これにより、処理する入力があるまでタスクが中断されます。

ランナウェイ タスクをキャッチするためのタイムアウト メカニズムもあるかもしれませんが、それは切り替えの主要な手段ではありません (そうでなければ、協調的ではありません)。

于 2013-05-11T05:02:01.447 に答える