linuxのC言語でforkしたりpipeでおしゃべりしたり
linuxだとforkってやつをよく聞くけれど、実際どんなもんなんだろうと思って息抜きがてら試してみた。
unistd.hで定義されているfork
って関数を呼ぶだけでプロセスを二つに分けられるらしい。
子プロセスには0が、親プロセスには子プロセスのpidが返るらしい。エラーだと-1らしい。
sys/wait.hのwait
って関数で子プロセスが止まるのを待てるらしい。
waitpid
を使えば特定の子プロセスを待てるらしい。
waitの引数はNULLかintへのポインタで、子プロセスの戻り値を受け取れるらしい。
プロセス間で通信するにはパイプを使うのが手っ取り早そうだ。
同じくunistd.hのpipe
関数でパイプを作れる。
戻り値は0なら成功、-1なら失敗。引数で渡したintが2つ分の配列に作ったパイプのファイルディスクリプタが入る。 0番目が読み込み、1番目が書き込み、のようだ。
作ったパイプはread
/write
で読み書きできる。
ファイルディスクリプタなんでselectとかも使えると思う。試してないけど。
まあ細かいことは面倒くさいのでソースコード。
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
// 子プロセス
// helloって送って、worldを受け取る。パイプを閉じて死ぬ。
// パイプを作っているのが親(というかフォーク前)なのだから、後片付けはwait後にやった方が自然な気がするけれど、まあ良いか。
void child(static int r, static int w)
{
char buf[1024];
write(w, "hello", 6);
printf("child> send> hello\n");
while(read(r, buf, sizeof(buf)) <= 0);
printf("child> recv> %s\n");
close(r);
close(w);
}
// 親プロセス
// helloを受け取って、worldを送る。そしたらパイプを閉じて死ぬ。
void parent(static int r, static int w)
{
char buf[1024];
while(read(r, buf, sizeof(buf)) <= 0);
printf("parent> recv> %s\n", buf);
write(w, "world", 6);
printf("parent> send> world\n");
close(r);
close(w);
}
void main()
{
int p2c[2], c2p[2];
// パイプを作る。双方向通信したいので2セット作る。
pipe(p2c);
pipe(c2p);
if(fork() == 0) // ここでフォーク。子プロセスが一つなのでpidは無視して、親か子かだけ判定。
{
child(p2c[0], c2p[1]);
}else
{
parent(c2p[0], p2c[1]);
wait(NULL);
}
return 0;
}
それなりの長さのソースコードに見えて、プロセス管理はforkとwaitの行の二つしかない。 パイプもpipeで作ってcloseで閉じてるだけだ。めっちゃ簡単。
windowsのプロセス生成は何だか面倒くさかった覚えがあるので、この手軽さはかなり魅力的かも。 イメージ的にはまるっとコピーするforkは処理が重たそうだけれど、wikipedia曰くコピーオンライトらしいからそんなに重くないのかもしれない。
ちなみに、親が子を待たずに死んでもtopコマンドでは0 zombieのままだった。 調べてみたら、こういうプロセスのことを孤児プロセスと言うらしい。凄いそのまんまなネーミングだ。
自分を生んだ親が死んだらinitプロセスを里親にするらしい。その名もリペアレンティング。日本語で最育成だとさ。 initプロセスが親になるので、死ぬときはinitに看取ってもらえる。なのでゾンビにはならないそうな。
まあそんなわけで。何だかとっても適当な感じの記事になってしまった。
参考: