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に看取ってもらえる。なのでゾンビにはならないそうな。
まあそんなわけで。何だかとっても適当な感じの記事になってしまった。
参考: