へっぽこ社会人4年生がプログラミングを頑張る

へっぽこ社会人4年目がプログラミング系統を中心に書きたいことをつらつらと書きます

システムコールで遊んでみる その4

今回は、システムコールforkwaitなどについて書いていこうと思います。 forkを呼び出すことで、 子プロセスの生成が出来ます。 また、waitを呼び出すことで、 親プロセスが子プロセスの終了を待ちます。 以下にプログラム例を示します。

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char **argv)
{
  pid_t pid;
  
  if((pid = fork()) == 0) {
    puts("child process");
    printf("pid:  %d\n", getpid());
    printf("ppid: %d\n", getppid());
    puts("exit child process");
    return 0;
  } else {
    puts("parent process");
    printf("pid:  %d\n", getpid());
    printf("ppid: %d\n", getppid());
    wait(NULL);
    puts("exit parent process");
  }
  
  return 0;
}
ちょっと解説(っぽいもの)

fork, getpid, getppidを利用するにはunistd.hを、waitを利用するためにはsys/wait.hをインクルードする必要があります。 forkは成功時に子プロセスには0を、 親プロセスには子プロセスのPIDを返し、失敗時は-1を返します。 getpid及びgetppidはそれぞれ呼び出し元のプロセスと親プロセスのPID(プロセスID)を返します。 getpid及びgetppidは常に成功します。 waitは成功時、終了した子プロセスのPIDを、失敗時に-1を返します。

fork, getpid, getppid, waitのインタフェースは以下の通り。

  • fork
    • pid_t fork(void);
  • getpid
    • pid_t getpid(void);
  • getppid
    • pid_t getppid(void);
  • wait
    • pid_t wait(int *stat_loc);

fork, getpid及びgetppidは引数を取りません。 waitの第1引数stat_locには、 子プロセスの終了ステータスが格納されます。 (第1引数stat_locintへのポインタ) 上記のプログラム例では、子プロセスの終了ステータスは不要なので、 NULLポインタが指定されています。

上のプログラム例では、forkの呼び出しで子プロセスを生成し、 それぞれ子プロセスと親プロセスで自身のPIDと親プロセスのPIDを表示します。 forkで子プロセスが生成されると、親プロセスと子プロセスの処理がそれぞれ並行で実行されます。 親プロセスは子プロセスの処理の完了をwaitで待ち、 子プロセスが終了したらwaitの後の処理を実行します。 以下に実行例を示します。

実行プログラム名はforkとします。
$ ./fork
parent process
pid:  1601
ppid: 465
child process
pid:  1602
ppid: 1601
exit child process
exit parent process
$ 

なお、forkは、扱い方によってはゾンビプロセスを生成する場合がありますので、応用する場合には注意が必要です。

(例. サーバプログラムなどで無限ループ中にforkで生成した子プロセスをwaitで待たずに生成してしまうような処理を含んでしまった場合)

今回は、並行処理を行うためのシステムコールforkと、 それに関するシステムコールをいくつか紹介しました。 詳しくはman 2 fork, man 2 wait, man 2 getpid, man 2 getppidとターミナルに入力し、マニュアルを呼び出してみてください。 次回はpipeについて書こうと考えています。 そんなわけで、今回はここまで。

参考文献