CFRunLoopSourceの使い方その1 [考え中の問題]
また仕事で、ちょっとプログラミングすることになった。その顛末は差し障りがあるのでここには書かないが、そのためのディテールの一部に(Cocoaではなく)Core Foundationのレベルでスレッドを扱う必要が出てきた。また結果を要求されているだけなので、そのうちの一般的な情報をここで整理しておく。MacOS XにおけるCore FoundationのCFRunLoopSourceを使う例を示すところまでやって一段落にするつもり。
どうでもいいが、ずっと僕がどうやって結果を得ているかというのは仙台に来てから全く問われたとこはない。たぶんエクセルとパワポンでできると思っているんだろう。Objective-CどころかMathematicaでのプログラミングさえまったく理解されない。ところがその上、結局得られた結果も最終的には信用されなくて、何かの決定に影響したことがないということにこないだ気がついた。僕はいったい何をしているんだろう。
具体的に言えば、unixでは新しいプロセスを起こすとき、forkとexecというシステムコール(OSの機能を呼び出すためのAPI)を連続して呼ぶことになる。forkでプロセスの実行環境のコピーを作り、その新しいプロセスの上でexecで実行コードを書き換えてそのコードに制御を渡す。unixではこの方法でしか新しいプロセスを作る手段が提供されていないが、これは他のシステムコールに較べて極端に重いものだった。fork/execを経由せずに、しかし別プロセスと同じような手法が使えたらそれはプログラマにとってうれしい。
そのためスレッドというのが導入された。これは独立したプロセスのように見えるけど同じ仮想空間を共有する。このためOS側はプロセスの実行環境(コンテクストと呼ぶ)をそれぞれに割り当てる必要は無く、オーバーヘッドは軽くなった。ちなみにスレッドの概念はBSDのアイデアだったらしく、僕が入社してunixを使いだした80年代はじめのBSD4.2には実装されていたが、同じ頃のSystemVにはなかった。
当初の実装では一つのプロセス内の複数のスレッド間の資源割り当てにOSは関与せず、user空間のライブラリによってスケジュールされていた。当時は「軽量プロセス(Light-Weight Process)」とも呼ばれた。とうぜん、スタックはそれぞれのスレッド用に個別に必要になるため、シングルスレッドに比べてメモリの利用効率は低下する。
そこでスレッドの制御をkernelに移すことが行われた。kernelはどのみちプロセスを制御してスケジュールするという作業をやっているので、同じメカニズムでスレッドも制御すればいい。スレッドとプロセスの違いは仮想空間を共有するか、別にするかだけだと考えればkernelが一元的に制御するように変更するのはそれほど大きな問題ではない。
ただし、スレッドの切り替えとプロセスの切り替えでは仮想空間をクリアするかどうか、という大きなオーバーヘッドの違いが発生する。さらにスレッドが発行するシステムコールによってkernel空間へのコンテクストスイッチも非同期的に発生するのでスケジューリングは階層化され複雑になった。
またkernelスレッドはプロセスほどではないにしても、kernelのリソース消費を増やすことになって(kernelのリソースはuser空間に較べてずっと高価な場合が多い)、必ずしも「軽量なプロセス」ではなくなっていった。
なんだか、読み物風になってしまったがもうしばらく続けることにする。
どうでもいいが、ずっと僕がどうやって結果を得ているかというのは仙台に来てから全く問われたとこはない。たぶんエクセルとパワポンでできると思っているんだろう。Objective-CどころかMathematicaでのプログラミングさえまったく理解されない。ところがその上、結局得られた結果も最終的には信用されなくて、何かの決定に影響したことがないということにこないだ気がついた。僕はいったい何をしているんだろう。
2 スレッドの歴史と位置づけ
まず、スレッドって何?というのを、小規模数値計算のプログラマとしての昔話を交えながらまとめる。2.1 スレッドの必要性
そもそもunixでスレッド(thread)は単に軽いプロセス(process)という位置づけだった。プロセスには仮想空間を生成するという非常にオーバーヘッドの大きな操作が必要になる。このおかげで安全なOSが実現できるようになったんだけど、unixでは小さなプロセスをたくさん実行させることが多く、このオーバーヘッドが馬鹿にならなくなった。具体的に言えば、unixでは新しいプロセスを起こすとき、forkとexecというシステムコール(OSの機能を呼び出すためのAPI)を連続して呼ぶことになる。forkでプロセスの実行環境のコピーを作り、その新しいプロセスの上でexecで実行コードを書き換えてそのコードに制御を渡す。unixではこの方法でしか新しいプロセスを作る手段が提供されていないが、これは他のシステムコールに較べて極端に重いものだった。fork/execを経由せずに、しかし別プロセスと同じような手法が使えたらそれはプログラマにとってうれしい。
そのためスレッドというのが導入された。これは独立したプロセスのように見えるけど同じ仮想空間を共有する。このためOS側はプロセスの実行環境(コンテクストと呼ぶ)をそれぞれに割り当てる必要は無く、オーバーヘッドは軽くなった。ちなみにスレッドの概念はBSDのアイデアだったらしく、僕が入社してunixを使いだした80年代はじめのBSD4.2には実装されていたが、同じ頃のSystemVにはなかった。
当初の実装では一つのプロセス内の複数のスレッド間の資源割り当てにOSは関与せず、user空間のライブラリによってスケジュールされていた。当時は「軽量プロセス(Light-Weight Process)」とも呼ばれた。とうぜん、スタックはそれぞれのスレッド用に個別に必要になるため、シングルスレッドに比べてメモリの利用効率は低下する。
2.2 kernelスレッド
user空間のスレッドでは、複数のスレッドが実行していたとしても、例えばひとつのスレッドがシステムコールを発行すると、システムコールから戻るまでそのプロセス上にある全スレッドが停止することになる。そこでスレッドの制御をkernelに移すことが行われた。kernelはどのみちプロセスを制御してスケジュールするという作業をやっているので、同じメカニズムでスレッドも制御すればいい。スレッドとプロセスの違いは仮想空間を共有するか、別にするかだけだと考えればkernelが一元的に制御するように変更するのはそれほど大きな問題ではない。
ただし、スレッドの切り替えとプロセスの切り替えでは仮想空間をクリアするかどうか、という大きなオーバーヘッドの違いが発生する。さらにスレッドが発行するシステムコールによってkernel空間へのコンテクストスイッチも非同期的に発生するのでスケジューリングは階層化され複雑になった。
またkernelスレッドはプロセスほどではないにしても、kernelのリソース消費を増やすことになって(kernelのリソースはuser空間に較べてずっと高価な場合が多い)、必ずしも「軽量なプロセス」ではなくなっていった。
なんだか、読み物風になってしまったがもうしばらく続けることにする。
2011-02-01 22:19
nice!(0)
コメント(0)
トラックバック(0)
コメント 0