forkを用いたプロセスのフォーキングについて。

§1 fork

  • forkをコールするとforkをコールしたプロセスの複製(子プロセス)が生成され、子プロセスはforkした部分から処理が継続する。
  • このとき、親プロセス側ではforkの戻り値として生成した子プロセスのプロセスID、子プロセス側では0が返却される(この戻り値を元に、プロセス自身がforkした親プロセスかforkされた子プロセスか判断し、処理を振り分ける)。
  • (子プロセスを生成するためのメモリが不足している等の原因により)子プロセスの生成に失敗した場合は、-1が返される。
  • 子プロセスを生成する際、親プロセスで開いているファイルディスクリプタは子プロセスにコピーされる。
  • forkされた後、子プロセスではforkされたスレッドのみしか存在しない状態になるためシングルスレッドで起動するが、mutexの状態などのpthreadオブジェクトは親プロセスのものがコピーされる。
  • 親プロセスのメモリロック、タイマー、非同期I/O、処理待ちのシグナルなどはコピーされない。

§2 exit

  • exitは、コールされたあとプロセスで使用していたリソースを解放しようとする。 exitを使って終了すると、親プロセスからコピーされたリソース(前述)を解放しようとする。
  • 親プロセスと共有しているリソースがある状態で子プロセスでexitすると、親プロセスがそのリソースを使用しているか否かに関わらず、リソースを解放してしまう。
  • exitに対して、_exitはリソースを解放せずに即時にプロセスを終了する(そのプロセスで開いたファイルディスクリプタは閉じられる)。

§3 wait

  • waitをコールすると、forkした子プロセスのいずれかが終了するまで待機する(このとき、waitをコールしたプロセスは実行が一時停止する)。
  • waitpidは、子プロセスの状態が変わった時点で実行が復帰する。
  • waitpidの第3パラメータにWNOHANGを指定すると、子プロセスの状態が変わっていなければ、即座に復帰する。
  • 第1パラメータに任意のプロセスIDを指定すると、プロセスIDに等しい子プロセスについて状態の変化を監視する。 -1を指定すると、子プロセスのいずれかの状態の変化を監視する。

§4 使用例

// gcc -std=c99

#include <stdio.h>
#include <sys/types.h> 
#include <sys/wait.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>

// 生成する子プロセス数
#define CHILD_MAX (5)


//
// ミリ秒単位のsleep
//
int msleep( int milliseconds )
{
  struct timespec t;

  t.tv_sec  =   milliseconds / 1000;
  t.tv_nsec = ( milliseconds % 1000 ) * 1000000;

  return nanosleep( &t, NULL );
}


//
// 親プロセスの処理
//
void parent_main()
{
  printf( "parent process %d started\n", getpid() );

  // 250ミリ秒間停止
  msleep( 250 );
}


//
// 子プロセスの処理
//
void child_main()
{
  printf( "child process %d started\n", getpid() );

  // 000ミリ秒間停止
  msleep( 3000 );
}


//
// 親プロセス
//
int main( void )
{
  for ( int count = 0; count < CHILD_MAX; count++ )
  {
    // 子プロセスを生成する
    pid_t pid = fork();

    if ( 0 == pid )
    {
      // 子プロセスの処理を開始する
      child_main();

      _exit( 0 );
    }
    else if ( -1 == pid )
    {
      printf( "fork failed\n" );

      return 1;
    }
  }

  // 親プロセスの処理を開始する
  parent_main();

  // すべての子プロセスの終了を待機する
  for ( int count = 0; count < CHILD_MAX; )
  {
    int status;
    pid_t pid;

    pid = waitpid( -1, &status, WNOHANG );

    if( 0 < pid )
    {
      printf( "child process %d exited\n", pid );
      count++;

      continue;
    }
    else if ( -1 == pid )
    {
      break;
    }

    // 500ミリ秒おきに子プロセスの状態を監視する
    msleep( 500 );
  }

  printf( "all processes are finished\n" );

  return 0;
}
実行例
child process 7729 started
child process 7730 started
child process 7731 started
child process 7732 started
child process 7733 started
parent process 7728 started
child process 7729 exited
child process 7730 exited
child process 7731 exited
child process 7732 exited
child process 7733 exited
all processes are finished