プロセスその20

これらの実装に関して詳細が一切無いが、OS小論:OSの構造をもう少し考えてみる(26)で触れた通り、x86系にはLOCK付きの命令を色々発行できるから、これを使ってMutexなりSemaphoreを構築していると想像される。ただ、元々Windowsはマルチプラットフォームを目指していたから、あまり特定のCPUに依存してしまうような複雑な構造はとっていないと思われる。

さて、もう少しこれらについて説明する。まず高速な順に書くと、Metered Sectionが最速である(前回書いたとおり、Critical Sectionは別だ。これはCommunication Methodとは言いにくい)。MSDNの記事にもあるように、Critical Sectionを応用しながら、リソースカウンタを備えている。これをサポートするため、Win32には、

LPMETERED_SECTION CreateMeteredSection() Metered Sectionの作成
LPMETERED_SECTION OpenMeteredSection() 作成済Metered Sectionへのオープン
DWORD EnterMeteredSection() 指定したMetered Sectionからスロット確保
BOOL LeaveMeteredSection() 指定したMetered Sectionにスロット返却
void CloseMeteredSection() 指定したMetered Sectionをクローズ

という関数が用意される。これの使い方だが、あるThreadがMetered Sectionを作成、それを複数のThreadがOpenして使う形になる。CreateMeteredSection()には、Initial CountとMaximum countが指定可能で、例えばInitial count=0, Maximum count=1なら単なるBinary Mutexとして動くし、Maximum count=2以上ならSemaphoreになるわけだ。Binary Mutexとして動く場合、あるTheradが最初にEnterMeteredSection()でスロットを取得すると、その後EnterMeteredSection()を発行してそのMetered SectionをアクセスしたThreadは、最初のThreadがLeaveMeteredSection()でスロットを返却するまでの間、ひたすら待機させられる事になる。

この構造に非常に近かったのが、VMSではEvent Flagである。OS小論:OSの構造をもう少し考えてみる(23)でちょっとだけ出てきたが、このEvent FlagというのはBinary Semaphoreの集合体と考えれば良い。Event FlagにはLEF(Local Event Flag)とCEF(Clobal Event Flag)という2種類があり、前者がProcess内、後者はSystem内での同期を取ることに利用される。ただ基本的な考え方は同じだ。

どちらのEvent Flagも64個づつ用意されており、いくらかはSystem Reserve(OSが内部で利用する)とされるが、ユーザーアプリケーションからも当然利用される。例えばCEFを使う場合、各プロセスは利用したいEvent Flagの番号を引数にSYS$ASCEFC(Associate Common Event Flag)を呼んで、利用権を獲得する。以後そのEvent FlagをセットしたければSYS$SETEF(Set Event Flag)を、クリアしたければSYS$CLREF(Clear Event Flag)をセットする。状態を知りたい場合は$READEF(Read Event Flag)という関数が用意される。ただこれはEvent Flagの本質ではない。メインとなるのはSYS$WAITFR(Wait for Single Event Flag)で、これは先のEnterMeteredSection()に近い、「Event Flagがセットされるのを待つ」という処理になる。この関数のオプションにタイムアウト(一定時間後にタイムアウト処理を行う)を指定できるのも非常に似ている。面白いのはEnterMeteredSection()、タイムアウトの引数に0を指定するとMetered Sectionの状態を判断して直ぐに戻るという、SYS$READEFの動作を行うことだ。またINFINITEを指定すると、タイムアウト無しでずっと待機するという、SYS$WAITEFと同じ処理になる。VMSではさらにSYS$WFLOR(Wait for Logical OR of Event Flag)とSYS$WFLANDWait for Logical AND of Event Flag)なんてものまで用意されていたが(この場合Bitmaskの形でEvent Flagを指定すると、複数のEvent Flagの状態をまとめて監視してくれた)、流石にこれは継承されていないようだ。

この2つが良く似ているのは、どちらもOSの中で最速であることだ。VMSの場合、Event FlagはI/Oの同期処理などに使われていた。アプリケーションがデバイスを指定して$SYS$QIOを発行すると、ただちにIPLが引き上げられ、I/O処理が始まる事はOS小論:OSの構造をもう少し考えてみる(7)で触れた通りだが、当然アプリケーションはどこかでI/O完了の同期を取る必要がある。これを行うのが実はEvent Flagである。I/O処理が終わると、KernelはそのProcessのLocal Event FlagをSetしてこれを通知する。そこで、アプリケーションはSYS$QIOの後でSYS$WAITEFを必ず発行して、同期を取ることになっている(OS小論:OSの構造をもう少し考えてみる(7)などで触れたASTはオプション扱いである。アプリケーションから見ればASTは高いIPLで動く事になり、他のプロセスに優先して処理が行われるために高速と言えるが、システム全体としてみるとIPLを上げるに伴いContext Switchingが余分に発生することになるから、その分のLatencyが加算されてしまい、最速とは言えない事に注意)。実際これをまとめたSYS$QIOW(内部でSYS$QIOを呼んだ後、続いてSYS$WAITEFを呼んでいる)なんて関数もあったほどだ。

追記:読者から「Metered SectionはWin32 APIそのものの機能ではなく、MSDNの記事は既存のWin32 APIを使いMetered Sectionを作ろうという趣旨である」旨をご指摘頂きました。確認したところ、まさしくその通りでしたのでお詫びして訂正させていただきます。