前回は、プロセス管理の基礎編ということで、プロセスのライフサイクルについて、fork,exec,wait,exit システムコールについてざっくりと理解していきました。
今回もプロセス管理にまつわる周辺知識を整理していきたいと思います。
プロセス間通信
まずは、シグナルやパイプなどのプロセス間通信について追っていきます。
シグナル
シグナルは、プロセスの正常な流れに割り込んで、強制的に中断または終了させる仕組みです。
まずは、ソースコードでシグナルの種類を見ていきましょう。
シグナルの種類
unix-v6/param.h at master · hephaex/unix-v6 · GitHub
ざっと、こんな種類がある。
Ctrl+Cで強制終了なんかはよくしていると思いますが、SIGINTがそれです。意外と身近なシグナルです。
また、プロセスを殺すときにkillコマンドを打ちますが、それもシグナル。
/* * signals * dont change */ #define NSIG 20 #define SIGHUP 1 /* hangup */ #define SIGINT 2 /* 割り込み*/ #define SIGQIT 3 /* 中断 */ #define SIGINS 4 /* 不正な命令*/ #define SIGTRC 5 /*トレース */ #define SIGIOT 6 /* iot */ #define SIGEMT 7 /* emt */ #define SIGFPT 8 /* 浮動小数点演算例外*/ #define SIGKIL 9 /* 強制終了 */ #define SIGBUS 10 /* bus error */ #define SIGSEG 11 /* segmentation violation */ #define SIGSYS 12 /* システムコールへの不正パラメータ */ #define SIGPIPE 13 /* end of pipe */
シグナルの送信と受信
unix-v6/proc.h at master · hephaex/unix-v6 · GitHub
proc構造体にはp_sig
というメンバがいて、シグナルを受信したプロセスは、p_sig
に受信したシグナルの種類を格納します。
struct proc { char p_stat; char p_flag; char p_pri; char p_sig;
処理内容
シグナルの種類を確認しましたが、シグナルを受け取ったプロセスは必ずそのシグナルの種類に応じた処理を実行しなければいけないわけではありません。
処理内容は大きく3つに分かれます。
- シグナルを無視
- シグナルを捕捉し、処理
- デフォルトを実行
シグナルハンドラといって、シグナルを受信したときに行う処理をプロセスに設定することができます。
パイプ
続いては、パイプです。
パイプも普通にターミナルで「|」で繋いで、出力結果をごにょごにょする時がありますが、あれです。
パイプの実体
パイプの実体は、ファイルです。
データを送る側のプロセスは、パイプにwriteシステムコールを発行して送るデータを書き込みます。
で、データを受信する側は、パイプからreadシステムコールでデータを読み込みます。
意外やシンプル。
プロセスに割り当てられるメモリ
続いては、 プロセスに割り当てられるメモリについて理解していきます。 プロセスには、テキストセグメントとデータセグメントという2つのメモリが割り当てられます。
exitシステムコールのときに、テキストセグメントとデータセグメントを解放しましたよね。
テキストセグメント
テキストセグメントは、プログラムの命令列が格納されます。
プログラムの命令列なので、ReadOnlyです。
データセグメント
データセグメントには、変数などのデータが格納されます。
テキストセグメントは、他のプロセス同士で共有されますが、データセグメントはプロセス間では共有されません。
さらにデータセグメントは、以下の3つの領域があります
- PPDA(per process data area)
- データ領域
- スタック領域
PPDA(per process data area)
PPDAには、カーネルスタック領域という領域があります。ここは、カーネルモードの作業領域として使用されます。
なーるほど、カーネルモード用のメモリはプロセスごとに持っているんすね。
データ領域
ここには、グローバル変数やスタティックな変数、さらにヒープ領域などがある。
スタック領域
ここには、関数の引数やローカルデータなどの格納される。
コンパイラを作った時にも、関数の引数やローカルデータなどはスタックに積んでポップして処理していったことを思い出しました。
スワッピング
メモリには容量があるので、すべてのプロセスのテキストセグメントとデータセグメントをメモリに格納しておくことはできません。
そこで、休眠状態にあるプロセスなどを一時的に外部ディスクに退避することで、メモリを有効に使っています。
これがスワッピングです。
と呼びます。
さて、このスワッピングですが、テキストセグメントとデータセグメントでは扱われ方が違います。
テキストセグメントは、スワップ領域に残る
テキストセグメントは、スワップインされてもスワップ領域に残り続けます。
テキストセグメントはReadOnlyデータであるので、メモリとスワップ領域でデータに違いがないからですね。
違いがないなら、元の方を残しておいて、移動先で不要になったら解放したらいいよね、という発想です。
データセグメントは、スワッピングの度解放
一方、データセグメントは、スワッピングの度に元のデータは解放されます。
まとめ
今回は、プロセス間通信とメモリについてざっと理解していきました。
OSの概観をざっくりと理解した後に、それぞれの機能について深ぼっていくと知識が繋がっていく感じがしてよいですね。
たとえば、exitシステムコールのメモリ解放で行っていたテキストセグメントとデータセグメントの解放がここで理解できました。
続いては、プロセススケジューラについて理解していきたいと思います。
僕から以上。あったかくして寝ろよー