スケジューラアクティベーション続き。4.3章のI/Oした時のカーネル/ユーザを
またぐ回数の記述が納得できない。スケジューラアクティベーションならI/Oで
ブロックしてそれが再開されるまでの間にブロックしたことを知らせるアップ
コールが一度は入らないといけない。これがなければI/Oでブロックしたスレッ
ドコンテキストを別のスレッドに移すことができないから。I/Oが完了した時は
運がよければ一回ですむ。なのでカーネルスレッド2回に対してスケジューラア
クティベーションは最低でも3回だろう。
Scheduler Activations: Effective Kernel Support for the User-Level Management of Parallelism Thomas E.Anderson, Brian N.Bershad, Edward D.Lazowska and Henry M.Levy University of Washington ACM Transactions on Computersystems, Vol.10, No.1, February 1992,p53-79. カーネルのプロセッサ割当てはそれぞれのアドレス空間がより大くのプロセッ サを使えるか、あるいは遊んでいるプロセッサがあるかの情報についてだけ知っ ていればよい。(アプリケーションはどちらの状態でもないこともある。例えば、 プロセッサを要求し、それを受けとり、さらにプロセッサを要求することがな い場合) 3.2章で述べたインターフェースによってスケジューラアクティベーショ ンを使うアドレス空間からこの情報が提供される。カーネルスレッドを直接使っ ているアドレス空間では、カーネル内部データによってこの情報は提供される。 スケジューラアクティベーションを使っているアドレス空間にはプロセッサは アップコールを通してユーザ層のスレッドスケジューラに割当てられる。カー ネルスレッドを使っているアドレス空間には元のTopazスレッドスケジューラに プロセッサが割当てられる。結果として、プロセッサの静的な分割の必要がな い。 4.2 スレッドスケジュールの方針 我々の設計の重要な面は、カーネルはアプリケーションの並行性モデルやスケ ジュール方針、ユーザ層を並行性を管理するデータ構造について関知しないこ とだ。アプリケーションのそれぞれは、これらについて適切なものを完全に自 由に選ぶことができ、そのアプリケーションに合わせて最適化することもでき る。FastThreadsの規定の方針はプロセッサ毎の実行待ち行列を各自のプロセッ サがLast-In-First-Outの順番である。これはキャッシュ局所性のためで、プロ セッサは自分の実行待ち行列が空になったら仕事を探しにいく。これは本質的 にMultilisp[10]で使用されている方針である。 追加として、我々の実装では不必要なプロセッサの再割当てを防ぐために、履 歴効果を含んでいる。アイドル状態になったプロセッサはそれをカーネルに再 割当て可能と通知する前に、小時間スピンする。 4.3 性能向上 ここまでで述べた設計で、ユーザ層スレッドにカーネルスレッドと同等の機能 を提供するのには十分だが、その性能に対して重要ないくつかの考察がある。 これらのうち一番重要なのは3.3章で述べたきわどい領域に関する部分である。 ユーザ層スレッドが横取りされた時(あるいはカーネル内でブロックしたり、再 開可能になったとき)、きわどい領域を一時的に継続させるために、ユーザ層ス レッドシステムはスレッドがロックを保持しているかどうかを調査できるよう になっていないとならない。これの一つの方法として、きわどい領域に入った らスレッドにフラグを立て、出る時にクリアする、そしてこれで継続している かどうかを調べる。一時的に継続して実行されたスレッドが、それが安全な場 所まで来た時、プロセッサを放棄して元のアップコールに戻るのに、その調査 が必要である。あいにく、これはページフォルトや横取りが起きても起きなく ても(これらはそう頻繁ではないにもかかわらず)、ロックの獲得と解除のオー バーヘッドが大きい。これらの頻繁に起こるきわどい領域をユーザ層スレッド システムの構築に使っているので、遅延時間はとりわけ重要である。 我々は一般的な場合にオーバーヘッドのない別の解決法を採用した。関連する 技術は、単一プロセッサ上のTrellis/Owlのガベージコレクタ[17]で使われてい た。低層のすべてのきわどい領域の正確なコピーを作る。これは特別なアセン ブラのラベルを使って、ユーザ層スレッドシステムのCのソースコード内のきわ どい領域のそれぞれの区切りでこれを行なった。そして、コンパイラの生成し たアセンブリコードを後処理して、コピーをした。これは言語とコンパイラの 支援があれば素直にやれるだろう。コピーの終了で、元の版とは違うきわどい 領域、プロセッサを放棄して再開するところに返るコードを置く。普通の実行 では元のコードを使う。横取りが起きると、カーネルは新しいスケジューラア クティベーションをユーザ層への通知のために開始する。このアクティベーショ ンは横取りされたスレッドのプログラムカウンタが、これらのきわどい領域の どれかに該当しているかを調べ、もしそうであれば、対応するこれらのきわど い領域のコピーの一つからスレッドを継続する。そのコピーによってきわどい 領域が終わったところで制御を放棄し、元のアップコールに戻る。普通の実行 では元のコードを使い、これは横取りに対処する処理を入れる前とまったく同 じコードなので、通常の場合ではまったくロックの遅延がない。(我々の実装で は、時折きわどい領域の中で手続き呼出しをしなくてはならない。この場合、 直線経路ではなく、明示的なフラグをセット/クリアすることによって呼出しを ひとまとめにする。) 二つ目の性能向上に重要なことは、スケジューラアクティベーションの管理に 関連することである。論理的にはそれぞれのアップコールに対して新しいスケ ジューラアクティベーションが作成される。新しくスケジューラアクティベー ションを作成するのには、そのデータ構造を割当て、初期化することが必要な ため、制約がないわけではない。その代わりに廃棄されたスケジューラアクティ ベーションを、いずれ再利用される時のためにとっておくことができる。ユー ザ層スレッドシステムは、それまで実行していたスレッドがそのコンテキスト から削除されたらすぐにそのアクティベーションをカーネルに返すことで、古 いスケジューラアクティベーションを再利用することができる。横取りの場合 では、ユーザ層に横取りを通知するアップコールを処理した後に。カーネル内 でブロックした場合では、ユーザ層に再開が可能であることを通知するアップ コールの処理をした後に。似たような最適化は多くのカーネルスレッドの実装 で使われている。カーネルスレッドが一度作成されたら、破棄する時でもとっ ておいて、この先のスレッドの作成を速くする[13]。 さらに、廃棄されたスケジューラアクティベーションを、一回ごとに返すので はなく、集めておいてカーネルに一括して返すことができる。時折の廃棄の一 括返還を無視して、我々のシステムではI/Oやプロセッサ横取りでアプリケーショ ン-カーネル境界をまたぐ回数を、伝統的なカーネルスレッドのシステムと同じ にしている。カーネルスレッドのシステムでは、I/Oの初まりで一回、そして I/Oの終了でもう一回、(カーネル/ユーザ)境界を越える。我々のシステムでも 同じカーネル境界の通過がある。 4.4 デバッグの考慮 Firefly Topazデバッガにスケジューラアクティベーションを統合した。二つの 分けられた環境があり、それぞれに要求がある: ユーザ層スレッドシステムの デバッグと、スレッドシステムの上で動くアプリケーションのデバッグである。 透過性はデバッグには重要である。デバッガはデバッギの命令の並びにできる 限り影響がないようにするべきだ。前述のカーネルサポートではユーザ層スレッ ドシステムにそれぞれの物理プロセッサの状態を通知するが、これはスレッド システム自体がデバッグの対象となる場合には不適切である。代わりに、カー ネルはそれぞれのスケジューラアクティベーションにデバッグする論理プロセッ サを割当てる; デバッガがスケジューラアクティベーションで停止あるいはシ ングルステップをした時には、これらの事象はユーザ層へのアップコールの対 象とならない。 ユーザ層スレッドシステムが正しく動いていると想定すると、デバッガはスレッ ドシステムの機能を使って、ユーザ層スレッドのコンテキストで走行している アプリケーションの状態を停止したり、調べることができる。
