§1 POSIX Semaphore

  • POSIXセマフォには、名前付きのセマフォと名前なしのセマフォがある。
  • 名前付きセマフォの操作関数は次のとおり。
    • sem_open (名前付きでセマフォを作成)
    • sem_wait (セマフォに対してP操作)
    • sem_post (セマフォに対してV操作)
    • sem_close (セマフォを閉じる)
    • sem_unlink (セマフォをシステムから削除)
  • 名前付きセマフォは、sem_openで作成する。 このとき、/dev/shm下にはsem.(セマフォの名前)という名前のファイルが作成される。
  • 名前付きセマフォの作成に際して、sem_openの引数modeを設定することにより、名前付きセマフォに対する書き込み/読み込みのアクセス許可を設定することができる(すでに作成されるセマフォにアクセスするには、セマフォへの書き込み/読み込みの両方が許可されている必要がある)。
  • 名前付きセマフォは、バージョン2.6以降のカーネルで使用可能(それより前のバージョンでは、名前なしのセマフォのみサポートされている)。
  • 名前付きセマフォの名前は、/で始まる必要がある。
  • sem_getvalueを使用すると、現在のセマフォのカウントを取得することができる。
  • sem_trywaitを使用すると、セマフォのカウンタが0になっていても実行がブロックされず、即座に復帰する(このとき、errnoにはEAGAINが設定される)。 カウンタが1以上の場合は、sem_waitと同等の動きとなる。
  • 名前なしのセマフォの場合は、sem_open/sem_closeの代わりにsem_init/sem_destroyを使用する。
  • 名前なしのセマフォの場合はカーネルでは管理しないため、使用しなくなってもsem_unlinkはコールしない。
  • POSIXセマフォ以外にも、SystemVセマフォが存在する

§2 使用例

// gcc -pthread -std=c99

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


// セマフォに与える名前
#define SEMAPHORE_NAME ("/interproc_semaphore_sample")

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

// セマフォオブジェクト
sem_t* semaphore = NULL;


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

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

  return nanosleep( &t, NULL );
}


//
// 子プロセスの処理
//
void child_main()
{
  pid_t pid = getpid();
  int semval = 0;

  printf( "ProcessID %d, started\n", pid );

  // セマフォのカウンタを表示する
  sem_getvalue( semaphore, &semval );

  printf( "->semaphore count: %d\n", semval );

  printf( "ProcessID %d, wait for  semaphore\n", pid );

  // セマフォに入る
  sem_wait( semaphore );

  printf( "ProcessID %d, access to semaphore\n", pid );

  // 適当な時間だけ待ち合わせる
  msleep( 1500 );

  printf( "ProcessID %d, release   semaphore\n", pid );

  // セマフォを出る
  sem_post( semaphore );

  // セマフォのカウンタを表示する
  sem_getvalue( semaphore, &semval );

  printf( "->semaphore count: %d\n", semval );

  printf( "ProcessID %d, exited\n", pid );
}


//
// 親プロセス
//
int main( void )
{
  // 初期値3のセマフォを作成する
  // (すでに同名のセマフォが存在する場合は、modeとvalueは無視される)
  semaphore = sem_open( SEMAPHORE_NAME, O_CREAT | O_EXCL, 0777, 3 );

  printf( "semaphore created\n" );

  // 250ミリ秒おきに子プロセスを生成する
  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;
    }

    msleep( 300 );
  }

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

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

    if( 0 < pid )
    {
      // 子プロセスが終了した
      count++;

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

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

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

  // セマフォを破棄する
  sem_close( semaphore );

  printf( "semaphore closed\n" );

  // セマフォを削除する
  sem_unlink( SEMAPHORE_NAME );

  printf( "semaphore unlinked\n" );

  return 0;
}
実行例
semaphore created
ProcessID 7862, started
->semaphore count: 3
ProcessID 7862, wait for  semaphore
ProcessID 7862, access to semaphore
ProcessID 7863, started
->semaphore count: 2
ProcessID 7863, wait for  semaphore
ProcessID 7863, access to semaphore
ProcessID 7864, started
->semaphore count: 1
ProcessID 7864, wait for  semaphore
ProcessID 7864, access to semaphore
ProcessID 7865, started
->semaphore count: 0
ProcessID 7865, wait for  semaphore
ProcessID 7866, started
->semaphore count: 0
ProcessID 7866, wait for  semaphore
ProcessID 7862, release   semaphore
ProcessID 7865, access to semaphore
->semaphore count: 0
ProcessID 7862, exited
ProcessID 7867, started
->semaphore count: 0
ProcessID 7867, wait for  semaphore
ProcessID 7863, release   semaphore
->semaphore count: 1
ProcessID 7863, exited
ProcessID 7866, access to semaphore
ProcessID 7868, started
->semaphore count: 0
ProcessID 7868, wait for  semaphore
ProcessID 7864, release   semaphore
->semaphore count: 1
ProcessID 7864, exited
ProcessID 7867, access to semaphore
ProcessID 7869, started
->semaphore count: 0
ProcessID 7869, wait for  semaphore
ProcessID 7870, started
->semaphore count: 0
ProcessID 7870, wait for  semaphore
ProcessID 7871, started
->semaphore count: 0
ProcessID 7871, wait for  semaphore
ProcessID 7865, release   semaphore
->semaphore count: 1
ProcessID 7865, exited
ProcessID 7868, access to semaphore
ProcessID 7866, release   semaphore
->semaphore count: 1
ProcessID 7866, exited
ProcessID 7869, access to semaphore
ProcessID 7867, release   semaphore
->semaphore count: 1
ProcessID 7867, exited
ProcessID 7870, access to semaphore
ProcessID 7868, release   semaphore
->semaphore count: 1
ProcessID 7868, exited
ProcessID 7871, access to semaphore
ProcessID 7869, release   semaphore
->semaphore count: 1
ProcessID 7869, exited
ProcessID 7870, release   semaphore
->semaphore count: 2
ProcessID 7870, exited
ProcessID 7871, release   semaphore
->semaphore count: 3
ProcessID 7871, exited
all processes are finished
semaphore closed
semaphore unlinked