UNIXプログラミング入門~プロセス編(1/4)~
c/c++でunix系osプログラミングを行うときに使うunistd.h
やその周辺のライブラリのお話その3です。
このメモでは、プロセス周りのシステムコールAPIに関して取り上げます。
プロセスとスレッド
プロセス
とスレッド
って何が違うの?
超簡単に表現すると同じメモリ資源を共有するかどうかです。
プロセス
それぞれが独立したメモリ資源、カーネル資源を与えられたプログラム。
スレッド
共通のメモリ資源、カーネル資源を扱うプログラム。
プロセスの親子関係
プロセスには通常、親子関係があります。
unix系osでは、カーネルの初期化後にinitプロセス
と呼ばれるプロセスが起動します。
以降起動されるプロセスはinit
をルートとするツリー構造で表現されるようになります。
pstree
コマンドを実行するとプロセスがどのような親子関係を持っているか確認できます。
マルチプロセスプログラミング
プロセスID
POSIXのプロセスには、正の整数でそれぞれにIDが割り振られている。
その番号をプロセスIDと呼びます。
プロセスIDはtop
コマンドやps
コマンドで確認できます。
ps aux
ですべてのプロセス情報を表示できます。
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 8324 144 ? Ss 16:40 0:00 /init ro
root 3 0.0 0.0 8332 152 tty1 Ss 16:40 0:00 /init ro
user1 4 0.0 0.0 15040 3480 tty1 S 16:40 0:00 -bash
user1 39 0.0 0.0 15664 1844 tty1 R 17:13 0:00 ps aux
子プロセスを生成する-fork(2)-
呼び出し元のプロセスを複製して子プロセスを生成するfork
関数のプロトタイプはunistd.h
に以下のように宣言されています。
pid_t fork(void);
fork
の呼び出し以降の処理は二つのプロセスで実行されることに注意してください。
新しく生成された子プロセス側はfork
の戻り値として0
が返されます。
呼び出し元の親プロセス側には新しく生成された子プロセスのプロセスIDが返ります。
プロセスの生成に失敗した場合は親プロセス側に負の数が返ります。
例えば、以下のプログラムを実行してみいてください。
#include <unistd.h>
#include <stdio.h>
int main(void)
{
pid_t pid = fork();
printf("%d\n", pid);
return 0;
}
数字が二回表示されるはずです。0
が表示されているのが子プロセス側です。
一般的には以下のように、条件分岐で親プロセス側と子プロセス側で動作を分けるような利用のされ方をします。
int main(void)
{
pid_t pid = fork();
if (pid < 0)
{
//例外処理
return -1;
}
if (pid > 0)
{
//親プロセスにやらせたい処理
}
else
{
//子プロセスにやらせたい処理
}
}
fork爆弾
察しが良い人は気づいたかもしれませんがfork
を実行すると実行のたびにプロセスが増えます。
なので、以下のようなコードを間違って書いてしまうと大変なことになってしまいます。
int main(void)
{
while(1)
{
fork();
}
}
俗にfork爆弾
と呼ばれネズミ算のように倍々にプロセスが増えていきシステムがダウンする恐れがあります。
軽い気持ちでは絶対に実行しないでください。
どれか一つの子プロセスの実行を待つ-wait(2)-
実行中のどれか一つの子プロセスの実行完了を待つ関数wait
のプロトタイプは<sys/wait.h>
に以下のように宣言されています。
pid_t wait(int *status);
第一引数には子プロセスの終了ステータス(※1)を格納するint
のポインタを渡します。
戻り値は実行が完了した子プロセスのプロセスIDです。
※1 終了ステータス…main関数の戻り値 又は exit
関数に渡した引数
任意の子プロセスの実行を待つ-waitpid(2)-
実行中の任意の子プロセスの実行完了を待つ関数waitpid
のプロトタイプは<sys/wait.h>
に以下のように宣言されています。
pid_t waitpid(pid_t pid, int *status, int options);
この関数は第三引数に渡す値によって挙動が変わります。
ここでは第三引数に0を渡した場合のみ解説します。
第一引数には待機する子プロセスのプロセスIDを渡します。
第二引数には子プロセスの終了ステータスを格納するint
のポインタを渡します。
第三引数には待機時のオプションを指定します。
戻り値は実行が完了した子プロセスのプロセスIDです。
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
int main(void)
{
pid_t pid = fork();
if(pid < 0)
{
printf("子プロセスの作成に失敗しました\n");
return -1;
}
if(pid > 0)
{
int stat;
pid_t p = waitpid(pid, &stat, 0);
printf("計算結果は %d です\n", stat);
}
else
{
size_t i, j=0;
for(i = 0; i < 1000; i++)
{
j += i;
}
printf("計算が終了しました\n");
return j;
}
return 1;
}
このプログラムでは子プロセスで計算が終わるまで待機して計算結果を表示します。
出力
計算が終了しました
計算結果は 11264 です
最後に
次回、もう一種類関数を紹介して自作bash風
プログラムを作成してみます。