サービスとプロセスの違い
分かりやすく整理してくださっているページがあったので共有。
サービスとは縁の下の力もちです。常にバックグラウンドで動いていて、要求があれば応答します。何もしないときも動いていますがじっと待っています。分かりやすいのはIISWebサービスです。ブラウザから要求があるまでじっと待っていて、要求があればhtmlファイルなどを返します。サービスの実態はプロセスです。よってサービスとプロセスは別物ではありません。サービスはプロセスから構成されています。しかしプロセスは必ずサービスではありません。例えばOfficeのプログラムの実態はプロセスです。しかしサービスではありません。主な違いは以下の比較表の通りです。
太字の箇所が重要でしょうか。
表の「操作」の行も重要なポイントかもしれません。サービスはSCM経由で制御される、プロセスはユーザからの入力をダイレクトに受けとることができる、ですかね。
プロセスとスレッドの違い
プロセスってなんとなく分かるけど、スレッドと何が違うのってところも整理しておきます。
こちらもこのページがわかりやすかったので共有です。
僕らが普段使う PC、スマホやサーバーで利用される OS のほとんどは、マルチタスクと呼ばれる機能を持っています。この機能があるおかげで、音楽を聞きながらインターネットを見てる時に電話の着信が来るように、複数のアプリが同時に動くことができます。そんなマルチタスクの機能は "同時に動いているように見せているだけ" というようによく言われています。これはマルチタスクの OS であっても、厳密には CPU の数しかプログラムを同時に実行することはできないからです。CPU が1つであればそれぞれの瞬間には1つのアプリケーションしか動いていません。マルチタスクの機能によって 実行するアプリケーションをものすごく細かく切り替えることで "同時に動いているように見せる" ことができます。※でもホントに同時に動いてるように見えるのは、いろんな所にあるバッファのおかげです。
このとき、実行されているアプリケーションの1つ1つは "プロセス"という名前で管理されます。なので、マルチタスクOS は「実行するプロセスをものすごく細かく切り替えている」ということになります。途中、メモリに関する記載があり、知っておいたほうがよいのは間違いないのですが、本まとめの趣旨からはすこし逸れるので割愛します。
プロセスを fork して子プロセスを複数立ち上げることで、同じプログラムを並列に動かすことができます。子プロセスを立ち上げたときには子プロセス用のメモリ領域が新たに割り当てられます。そこに親プロセスのデータセグメントをコピーして、その子プロセス専用のデータセグメントを確保します。動かすプログラムの命令自体は親プロセスと全く同じなので、テキストセグメントは親プロセスと同じ領域を参照します。
実行するプログラム自体は親プロセスと同じデータを共有し、実行状態に関わる変数などのデータは専用のものを利用することになります。子プロセスも1つのプロセスとして扱われるので、他のプロセスのメモリに直接アクセスすることはできません。Webブラウザを何個か開いたときにそれぞれの画面で別々の操作ができる状態を想像すると分かりやすいです。
スレッド
プロセスからスレッドを生成することで同じプログラムを並列に動かすことができます。スレッドが生成された時には親プロセスの仮想アドレス空間内に、スタック領域・SP (スタックポインタ)・PC (プログラムカウンタ)の値をスレッド毎にコピーして利用します。それ以外のデータは全て親プロセスと同じものを利用します。スレッドは子プロセスとは違い自分専用のメモリ領域は用意してもらえません。つまり、親プロセスの領域の中に全てのスレッドが存在していることになります。そのスレッドがいまプログラムのどの部分を実行しているかという情報だけを持ち、その他のデータは全てのスレッド間で同じものを利用します。
ほぼ全てのデータを他スレッドと共有するので、あるスレッドが利用しようとしている変数が他のスレッドによって既に書き換えられていたり消されていたりする状況が発生します。この状況で動作しても問題が発生しないプログラムのことを、"スレッドセーフ" と言います。
ここがプロセスとの大きな違いです
プロセス間はアプリケーションの実行状態の情報を共有していないから、プロセス間通信をする際には大きなコストが掛かるけど、スレッド間通信はメモリ空間を共有してるから低コストですよってことですかね。
プロセスをたくさん立ち上げて動かす場合
プロセスはそれぞれに専用の仮想アドレス空間を保持しており、データセグメントのデータはそれぞれのメモリ領域内に保持されています。そのため、プロセスを切り替える場合は 仮想アドレス ⇔ 物理アドレス のマッピングをそのプロセス用のものへの切り替える必要が出てきます。
1度アドレスを解決した結果は MMU がキャッシュしているため、そのままの状態でプロセスを切り替えてしまうと前のプロセスの領域が見えてしまうことになります。なので、プロセスを切り替える時は MMU が持っているキャッシュをクリアする必要があります。※TLB フラッシュ と呼ばれます
この TLB フラッシュがパフォーマンスに与える影響はかなり大きいらしいです。どれくらい高コストなのかはよくわからないのですが、フラッシュ直後は全てキャッシュミスになるので 確かにコストが大きくなりそうな想像はできます。
スレッドをたくさん立ち上げて動かす場合
スレッドが持つデータは 全て親プロセスのメモリ領域内に保持されているため、利用する仮想アドレス空間を切り替える必要がありません。そのため TLB フラッシュは必要ありません。実行するスレッドを切り替える場合は、スタック領域、SP、PC の切り替えだけで済みます。
なので、単純に同じプログラムを並列で動作させる場合は、スレッドを利用した方が切り替えにかかる時間が短く より高速に処理できるということです
上で記載したとおり、スレッドの方がプロセスに比べ、はるかに切替えコストが小さいということです。
その他、知らない用語の整理
ゾンビプロセスと孤児プロセス
このページから抜粋。
もういないのに「まだいるよ状態」になっているプロセスのうち、既に親がいない状態のプロセスのこと
纏めると、
- プロセスが動作すると、管理台帳に名前が記録される
- 動作が終わると台帳から名前が消される
- 台帳を見れば、今どんなプロセスが動作しているかわかる
- 管理台帳に名前を書くのは名前を書かれるプロセスの親プロセス
- 時々、動作が完了しているにも関わらず残っているプロセスがある
- 動作終了直後で、これから名前を消すプロセス
- ゾンビプロセス
- 名前を消し忘れたプロセス
- 孤児プロセス
- 動作終了直後で、これから名前を消すプロセス
親プロセスから新たにforkして子プロセスを作る際、プロセステーブル(上記の台帳に該当)に子プロセスの内容が記載される。
子プロセスの処理が終了した時点ではまだプロセステーブルには子プロセスの名前が残っていて、この状態をゾンビプロセスと言っている。
親プロセスからwait()が発行されるとプロセステーブルから子プロセスが削除される。
親プロセスがwait()を発行せずに終了した場合、子プロセスだけ残ってしまい、孤児プロセスとなる。
全てのプロセスには必ず親子関係があるため、親がいなくなったプロセスは更にその親(このページでは長老といっている)に引き取られる。
※ページでは(Linuxの場合)initプロセスと言っているけど、どんな場合でも親が先に死んだ孤児プロセスの引取先は必ずinitプロセスになるんでしょうか??
おまけ
PyConJPの以下の動画がプロセスについてとてもわかりやすく説明しています。ご興味のある方は是非。
05-202_Pythonでざっくり学ぶUnixプロセス(tell-k) - YouTube https://www.youtube.com/watch?v=pX58pvc3Fk4&feature=youtu.be