2012-04-29

GNU/Linuxにおけるプロセス

GNU/Linuxでは、スケジュールの単位はプロセスである。スレッドというのは、ちょっと特殊なプロセスにすぎない。

Windowsでは、スケジュールの単位はスレッドである。プロセスというのは、スレッドを束ねる要素でしかない。たしかに、プロセスには優先度を設定できる。ただしこれは、スレッドの優先度が、スレッドに設定されている優先度とスレッドが属するプロセスに設定されている優先度から計算されるだけである。プロセスは実行の単位ではない。

プロセスを作るためのLinux Kernel APIとして、fork(), clone(), vfork()がある。Linux kernelでは、それぞれsys_fork(), sys_clone(), sys_vfork()として提供されている。また、その下にdo_fork()があって、実際の処理はこの関数がやっていたりする。ただし直接使うことはない。というのも、これらの関数は、いくつかの引数を普通に関数の引数として受け取るのではなく、定められた方法でスタックにつまれていたり、指定したレジスタに格納されていたりと、アーキテクチャごとに微妙に異なる引数の渡し方を要求する。そのため、glibcでは、使いやすくラップしたfork(), clone(), vfork()を提供している。ただし、現在のglibcの実装では、すべてclone()を使った実装になっている。

clone()というのは非常に柔軟なAPIで、プロセスを様々な状態で作成することができる。ただし、通常はclone()を直接使うことはない。使うのはカーネル外のライブラリでラップしたfork()やpthread_create()などである。

さて、とりあえずWindowsでは体験出来なかった、高速なforkの実装で遊んでみた。なるほど、確かに動く。

GNU/Linuxでforkが高速に動くのは、Copy-on-writeを使っていて、メモリを共有しているからである。子プロセスでは、書き込みされたストレージだけ、差し替えられる。ちなみに、Windowsではネイティブでforkを提供していないので、だいぶリベラルなことをしなければならない。cygwinの実装では、別プロセスをたちあげてロードされているモジュールをすべて読み込み、ハンドルを複製し、子プロセスのメモリを親プロセスから書き換えなどなど、相当な努力をしている。

しかし、LinuxベースのOSでPOSIXを実装するというのは、Linuxカーネルだけではなく、libcによるところも大きい。libcの最も有名な実装はglibcや、その派生物である。GNU/Linux、もしくはGNU+Linuxという言い方は、間違っていないというわけだ。

No comments: