プロセスとは?
こんにちは、iCAREサーバーサイドエンジニアの寺井(@krpk1900_dev)です。
iCAREではRails技術顧問の前島さんと一緒に毎週読書会を行っており、先日「なるほどUnixプロセス」を読み終えました。
そこで今回は、この読書会で新たに知った概念や知識について整理して記事にまとめたいと思います。
カーネルとユーザーランドとシステムコール
カーネルについては以前OSについて学習したときに概要を学んだのですが、ユーザーランドやシステムコールを含めた全体像について整理してみました。
1. カーネル
カーネルはOSの中核を構成するソフトウェアで、ハードウェアとソフトウェアの中間層に当たり、ハードウェアの制御やプロセスの管理などを担っています。
ファイルシステムの読み書きやスピーカーでの音楽再生などの制御はカーネルを通じて行われます。
パソコンを起動するとまずHDDにインストールされたOS(カーネル)プログラムが展開されますが、このときに使われるメモリ領域は「カーネル空間」と呼ばれます。
2. ユーザーランド
ユーザーランドはOS環境の中でカーネル以外の要素のことです。
一般ユーザー権限で操作できる領域であり、具体的にはシェルやコマンド、アプリケーションなどが含まれます。
OS上でプログラムが起動された場合はOSの管理によってプログラムがメモリに展開されますが、このときに使われるメモリ領域は「ユーザー空間」と呼ばれます。
3. システムコール
プロセスがOSに対して予期しない影響を与えることを避けるために、ユーザー空間上の一般的なプロセスはカーネル空間に直接アクセスできないように制限されています。
カーネル空間にアクセスするためには、OSが一般的なプログラム向けに提供しているシステムコールと呼ばれるAPIを利用することになります。
これによって、OSに対してはあらかじめ決められた動作のみが行われるように制限できます。
つまり、システムコールはカーネル空間とユーザー空間を繋いでいるインターフェースに当たります。
(Armadillo開発者サイトより引用)
プロセス
Unixではあらゆるプログラムがプロセスの上で実行されます。
Railsサーバもlsコマンドもzshもプロセスの上で実行されています。
プロセスの上で実行されるとは、例えばlsコマンドをターミナル上で実行すると、OSが新しいプロセスを1つ用意し、lsプログラムはそのプロセスの上で実行されるということです。
(『なるほどUnixプロセス』を読む前にちょっとだけナルホドとなる記事 Rubyist Magazineより引用)
1. 親プロセス
すべてのプロセスには親プロセスがいます。
大抵の場合、親プロセスはそのプロセスを起動したプロセスとなります。
例えば、ターミナルを起動したらzshプロンプトが表示される場合は、zshプロセスの親プロセスはターミナル.appプロセスになります。
さらにlsコマンドを実行したら、lsプロセスの親プロセスはzshプロセスになります。
すべてのプロセスは環境変数を親プロセスから引き継ぎます。
2. 終了コード
あらゆるプロセスは、0-255の終了コード値とともに終了します。
一般的には、正常終了時には終了コード0を返し、それ以外の終了コードは異常終了を表しています。
Rubyで
を実行したことはないでしょうか?exit
このとき実行されているのは
であり、終了コード0を返してプロセスを終了させています。Kernel#exit
一方で、
は終了コード1を返してプロセスを終了させるため、問題のあったプロセスを終了させる場合によく使われます。Kernel#abort
ちなみにkillコマンドやCtrl+Cはプロセスに対してシグナルを送信しています。
killコマンドで送っているシグナルはTERM(Terminate)、Ctrl+Cで送っているシグナルはINT(Interrupt)で、これらのシグナルをプロセスが受信してプロセスが終了します。
3. ゾンビプロセスと孤児プロセス
実行中のプロセスはプロセス管理テーブルに記録されているが、すでに実行が終了しているのにも関わらずプロセス管理テーブルに残ってしまっているプロセスを「ゾンビプロセス」と呼びます。
また、親プロセスがwait()を実行しないまま終了してしまい、プロセス管理テーブルから削除されることがなくなってしまった取り残された子プロセスのことを「孤児プロセス」と呼びます。
次の節で説明するデーモンプロセスは、意図的に孤児化させたプロセスに当たります。
4. デーモンプロセス
デーモンプロセスとは、ユーザーから端末によって制御されるのではなくバックグラウンドで動作するプロセスです。
デーモンプロセスはOSの核でもあり、GUIシステムのウィンドウや印刷サービス、スピーカーから通知を受け取るオーディオサービスが使えるのはデーモンプロセスがバックグラウンドで起動しているおかげなようです。
ちなみに、デーモンの名称には末尾にデーモンを表すdをつけるのが慣習となっているそうです。(systemd、tcpd、httpdなど)
systemdはTwitter GJ Carelyをデーモンプロセスにするときに使いました!
5. initプロセス
デーモンプロセスの中でも特別重要なプロセスがinitプロセスです。
カーネルが起動されたときに生成される一番最初のプロセスがinitプロセスであり、ほとんどのデーモンプロセスはinitプロセスが親プロセスになっています。
ちなみに、initプロセスのppid(親プロセスのid)は0であり、initプロセスがすべてのプロセスの始祖に当たることが確認できます。
終わりに
今回は、カーネルとユーザーランドとシステムコール、そしてプロセスについて学んだ内容を記事にしました。
プロセスについて学んだ過程で、Worker(ワーカー)やThread(スレッド)についても気になって調べたので、また改めて整理して記事にしたいと思います。
アプリケーションプログラムを書く業務にすぐに活かせることはもしかしたら少ないかもしれませんが、アプリケーションの負荷を軽減したりパフォーマンスを改善するなどのインフラよりの業務を行うときには必ず役に立つ内容だと思っているので、RubyやRailsの学習も進めながらより汎用的な領域の学習も継続していきたいと思います。