page1からpage21までで一応C言語(gcc)の基本的な部分を書いたつもりで、このページからは少し難しいことを扱っていきます。
このページでは新しいプロセスを立ち上げる方法について書きます。
新しいプロセスを立ち上げるにはforkを使用します。unistd.hのインクルードが必要です。
pid_t fork(void)
forkの戻り値は少し複雑です。forkは新しいプロセスを起動する関数ですので、成功した場合は親プロセスと子プロセス両方に値が返されます。子プロセスには0が、親プロセスには子プロセスのプロセスID(PIDなどと略されます)が返されます。失敗した場合は-1が返ります。
forkがどのような効果を持っているのか、実際に見てみましょう。
/* プロセス起動の例 */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char* argv[]){ sleep(60); fork(); sleep(60); return 0; }
何も出力しないプログラムです。sleepは指定秒数待機する関数で、このプログラムではforkの前後1分の待機時間を設けています。
システムモニタでプロセスを監視すると・・・(a.outが実行したプログラムです)
プログラム開始後1分間はこのような状態です。a.outが1つだけあります。1分経過後(fork()実行後)は次のようになります。
a.outが2つになりました。元からあった方(PID 10226)が親プロセス、新しくできた方(PID 10235)が子プロセスとなります。
プロセスを分けた後もなにもしなければプログラムは同じルートを通ります。
/* プロセス起動の例 */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char* argv[]){ printf("A\n\n"); fork(); puts("B"); return 0; }
プロセスを分ける前にAが出力され、プロセスが分けられた後、両方のプロセスでBが出力されます。これではどちらが親でどちらが子か分かりません。
親プロセスと子プロセスとで処理を分けるにはforkの戻り値を利用します。
/* プロセス起動の例 */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char* argv[]){ pid_t p; printf("A\n\n"); p = fork(); printf("B %d\n",p); return 0; }
プロセスforkの戻り値が親プロセスと子プロセスとで異なるため、Bの後ろの数字が異なります(子プロセスは0となります)。
これを利用して次のように処理を分けることができます。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char* argv[]){ int i,j,k; pid_t p; j = 50; p = fork(); if(p == 0){ /* 子プロセス */ for(i=0;i<5;i++){ /* 1秒ごとにAAAとjを加算した値を出力する */ printf("AAA %d\n",j++); sleep(1); } } else(p > 0){ /* 親プロセス */ for(k=0;k<5;k++){ /* 1秒ごとにBBBとjを減算した値を出力する */ printf("BBB %d\n",j--); sleep(1); } } else(p == -1){ /* エラー */ } return 0; }
1秒ごとにスリープしているので、同時に2つのプロセスが動いていることが分かります。jは変数名称は同じですが、プロセスが違うので片方は足される一方、もう片方は引かれる一方となります。