(私の質問の背景を最初に説明します。質問自体は一番下にあり、「質問」は太字で示されています)。
2つのプロセスAとBを例に挙げます。 Aは条件を確認し、条件が満たされていないことを発見した後、スリープ/遮断状態に入ります。 Bは条件を満たし、Aを目覚めさせる。すべてがこの順番で起こった場合、問題はありません。
スケジューラが次のことを行う場合:
- 確認条件が満たされない
- Bは条件を満たし、Aを目覚めさせる
- A省電力モードへの切り替え/遮断
その後、BはAのために行ったウェイクアップを失います。
私はブロックセマフォを実装する過程でこの問題に遭遇しました。これにソリューションを提供するいくつかのソースは次のとおりです。
Andrew Tanenbaum、最新のオペレーティングシステム、第4版、14ページ。 130:
ここでの問題の本質は、まだスリープモードに移行していないプロセスに送信されたウェイクアップが失われることです。何も失わなければ、すべてがうまくいくでしょう。クイック修正は、追加するルールを変更することです。気象待機ビット図に。このビットは、まだウェイクアッププロセスにウェイクアップが送信されると設定されます。後でプロセスがスリープモードに切り替えようとすると、ウェイクウェイトビットがオンの場合はオフになりますが、プロセスはアクティブのままです。ウェイクアップ待機ビットは、ウェイクアップ信号を記憶する貯金箱である。コンシューマはループが繰り返されるたびにウェイクウェイトビットをクリアします。
この記事はLinux Magazineに掲載されました。("Kernel Korner - Sleeping in the Kernel", Linux Journal #137) では、次のように述べています。
このコードはウェイクアップ損失の問題を防ぎます。どのように?条件をテストする前に、現在の状態をTASK_INTERRUPTIBLEに変更しました。それで何が変わったのですか?変更は、TASK_INTERRUPTIBLEまたはTASK_UNINTERRUPTIBLE状態のプロセスがwake_up_processを呼び出し、プロセスがまだSchedule()を呼び出さないたびに、プロセスの状態が再びTASK_RUNNINGに変更されることです。
したがって、上記の例では、プロセスBがlist_emptyをチェックした後、いつでもウェイクアップを実行しても、Aの状態は自動的にTASK_RUNNINGに変わります。したがって、Schedule()を呼び出すと、前述のようにプロセスAはスリープモードに移行せず、一定期間だけ予約されます。したがって、ウェイクアップはもはや失われません。
私が理解しているように、これは基本的に、「後でスリープモードをオフにすると、後でスリープ/ブロックの呼び出しをキャンセルできるように、プロセスをスリープ/シャットダウンしたいプロセスとしてマークできます」と言います。
ついにこの配布資料下のいくつかの段落には、「次の擬似コードは、ブロックセマフォと呼ばれるこれらのセマフォの実装を示しています。」では、セマフォをブロックするコードが提供され、原子演算「Release_mutex_and_block(csem.mutex);」を使用します。彼らは次のように主張します。
P()ing プロセスは自動的に実行不可能になり、ミューテックスを解放する必要があります。ウェイクロスの危険があるからだ。 release_mutex(xsem.mutex) と sleep() という 2 つの異なる操作がある場合を想像してください。 release_mutex() と sleep() の間でコンテキスト切り替えが発生すると、他のプロセスが V() 操作を実行し、最初のプロセスを dequeue_and_wakeup() しようとすることがあります。残念ながら、最初のプロセスはまだ眠っていないので、目覚めを見逃してしまいます。代わりに再実行するとすぐにスリープ状態になり、誰も目覚めません。
オペレーティングシステムは通常、ミューテックスをパラメータとして使用するsleep()システムコールフォーマットでこれらのサポートを提供します。その後、カーネルはミューテックスを解放し、プロセスを中断のない(または保護された)環境でスリープモードに切り替えることができます。
質問:UNIXのプロセスには、「スリープを計画しています」またはTanenbaumが言ったように、「ウェイクアップ待ちビット」としてマークする方法はありますか?ミューテックスをアトミックに解除してからプロセスをスリープモードに切り替える/ブロックするシステムコール sleep(mutex) はありますか?
明らかに私はシステムコールと一般的なオペレーティングシステムの内部に慣れていません。私の質問に明らかに誤った仮定や用語の誤用がある場合は、これを指摘していただきありがとうございます。
答え1
従来のUnixyソリューションは、待機プロセスが読み取ろうとするパイプにバイトを書き込むことで構成される「ウェイクアップ」信号を持つことです。
(プロセスにこの機能を設定するのに役立つ共通の祖先がない場合は、名前付きパイプを使用できます。)
その後、スタンバイプロセスはselect()システムコール(またはpselectまたはpoll / epollなどの最近再設計された代替手段の1つ)を使用できます。原子的に「該当するファイル記述子から読み取る準備ができるまで待ちます」を実行します。目が覚めたら、読もうとしていた内容をすべて読んで削除します。それからどのタスクが準備されているかを確認してください。