Operating Systems. Exercise 09

I. セマフォとミューテックスによる共有メモリ排他制御 (準備課題, 配点無し)

OSは複数ユーザを受け入れたり 複数のプロセスを同時に実行します。
リソースへの同時アクセスに対するリスクが常在するため, OSは一般に排他制御を行うための機構を備えています。

現在ではSemaphore(セマフォ)やMutex(ミューテックス)と呼ばれる機構が標準的に使われており,
共有メモリでの排他制御にはこれらの仕組みを利用することもできます。


Semaphore

セマフォは共有メモリなどの共有領域に確保されたカウンタとして実装されています。

カウンタの初期値は同時アクセスを許すプロセス数で, クリティカルな処理部分へ入るとカウンタが-1され,
その処理が終了すると+1される機構です。

カウンタが0のときは, 既に同時アクセスを許すプロセス数に達しているので,
資源を使用中の他プロセスが処理を終えてカウンタを加算してセマフォが0より大きくなるのを待ちます

プログラム内


セマフォ減算要求();//セマフォの値が0ならここで待たされる

//クリティカルな処理
...
...
...

セマフォ加算要求();
	      

Mutex

一般にMutexは同時アクセスを許すプロセス数が1のSemaphoreと考えられます。
アクセス制御の対象となる部分へは1つのプロセスしか同時にアクセスできません。


Semaphore(Mutex)による共有メモリアクセス制御

同時アクセスを許すプロセス数を1にしたSemaphore(Mutex)のテストです

SemaphoreTest.h

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

#ifndef SEM_R        //セマフォのパーミッションSEM_Rが定義されていないときは
#define SEM_R 000400 //パーミッションを定義する
#endif

#ifndef SEM_A
#define SEM_A 000200
#endif
      

SemaphoreTest.c

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "SemaphoreTest.h"

//プロトタイプを適宜

int nSemaphID;

int main(int argc, char *argv[]){
  int nPid;

  //Ctrl-cなどで中断されたときも後片付けするように
  signal(SIGINT, SignalHandler);
  signal(SIGQUIT, SignalHandler);
  signal(SIGTERM, SignalHandler);

  CreateSemaphore();
  system("ipcs");//作成したセマフォや共有メモリの状態を見るコマンド実行

  nPid = fork();
  if (nPid < 0) {//fork失敗
    perror("fork failure"); 
    exit(-1);
  }
  else if (nPid == 0) ChildProcess();
  else                ParentProcess();

  DiscardSemaphore();
  
  return 0;
}

//親子ともにPrintを5回呼び出す
void ChildProcess() {
  int i;

  for (i=0; i<5; ++i) {
    //    ReserveCriticalRegion(); //セマフォで排他制御
    Print("Child ");      //排他制御を行う関数
    //    ReleaseCriticalRegion(); //セマフォを元に戻す
  }

  exit(0);
}

void ParentProcess() {
  int i;

  for (i=0; i<5; ++i) {
    //    ReserveCriticalRegion();
    Print("Parent");
    //    ReleaseCriticalRegion();
  }

  wait(0);
}

void Print(char* sProcessName) {

  time_t currentTime;

  time(&currentTime);
  printf("%s:start time: %s", sProcessName, (char*)ctime(&currentTime));
  fflush(stdout);

  //ある程度時間がかかる処理を模している
  sleep(1);

  time(&currentTime);
  printf("%s:end   time: %s", sProcessName, (char*)ctime(&currentTime));
  fflush(stdout);

}

//セマフォ作成
void CreateSemaphore() {

  nSemaphID = semget(IPC_PRIVATE, 1, SEM_R|SEM_A|IPC_CREAT);

  if (nSemaphID == -1){
    perror("semget failure"); 
    exit(-1); 
  }

  //クリティカルな処理への同時アクセス可能なプロセス数を1に設定したセマフォを作る(Mutex)
  union semun semctlArg;
  semctlArg.val = 1;
  if (semctl(nSemaphID, 0, SETVAL, semctlArg) == -1) {
    perror("semctl(SETVAL) failure"); 
    exit(-1); 
  }

  printf("nSemaphID = %d\n", nSemaphID);

}

//セマフォ破棄
void DiscardSemaphore() {

  if (semctl(nSemaphID, 0, IPC_RMID, 0) == -1) {
    perror("semctl(del) failure"); 
    exit(-1); 
  }
}

//資源獲得要求
void ReserveCriticalRegion() {

  struct sembuf semBuf;
  semBuf.sem_num = 0;
  semBuf.sem_flg = 0;
  semBuf.sem_op = -1;//セマフォに減算する

  if (semop(nSemaphID, &semBuf, 1) == -1) {
    perror("semop(reserve) failure"); 
    exit(-1);
  }
}

//資源開放要求
void ReleaseCriticalRegion() {

  struct sembuf semBuf;
  semBuf.sem_num = 0;
  semBuf.sem_flg = 0;
  semBuf.sem_op = 1;//セマフォに加算する

  if (semop(nSemaphID, &semBuf, 1) == -1) {
    perror("semop(release) failure"); 
    exit(-1);
  }
}

void SignalHandler(int h){
  //後片付け
  DiscardSemaphore();
  exit(1);
}
      
上のソースコードでは, セマフォを実際に使用する部分をコメントアウトしてあります
排他制御を行っていないのでこのまま実行するとParentがstartしてendする前に
Childがstartしてしまったりと, 入り乱れた順番で処理されています

% ./SemaphoreTest
      

Parent:start time: Fri Jun 10 11:30:54 2011
Child :start time: Fri Jun 10 11:30:54 2011
Parent:end   time: Fri Jun 10 11:30:55 2011
Parent:start time: Fri Jun 10 11:30:55 2011
Child :end   time: Fri Jun 10 11:30:55 2011
Child :start time: Fri Jun 10 11:30:55 2011
.
.
.
      
ReserveCriticalRegion()とReleaseCriticalRegion()のコメントアウトを親と子両方で外し,
排他制御を行うようにして実行してみると一方がPrintを実行している間, もう一方はその部分の実行を待つようになります

% ./SemaphoreTest
      

Parent:start time: Fri Jun 10 19:25:47 2011
Parent:end   time: Fri Jun 10 19:25:48 2011
Child :start time: Fri Jun 10 19:25:48 2011
Child :end   time: Fri Jun 10 19:25:49 2011
Parent:start time: Fri Jun 10 19:25:49 2011
Parent:end   time: Fri Jun 10 19:25:50 2011
Child :start time: Fri Jun 10 19:25:50 2011
Child :end   time: Fri Jun 10 19:25:51 2011
.
.
.
      

II. 共有メモリとセマフォを使ったプロセス間通信(配点100%)

課題

共有メモリに塔, 円盤数, 移動回数を格納する変数を確保し, プロセス1がハノイの塔の問題を解き
プロセス2が現在の状態を出力するプログラムを書いてください。
ただし, 共有メモリ領域へのアクセスはセマフォによる排他制御で同期を取るようにしてください

ヘッダ(共有メモリ関連) : HanoiShareMem.h
ヘッダ(セマフォ関連) : HanoiSemaphore.h

ハノイの塔の円盤移動(Solve) (テンプレート)
ハノイの塔の状態を出力する(Display) (テンプレート)

:

ターミナル1 (プロセス1実行)

% ./HanoiSemaphoreSolve 30
Share Memory ID = 15695884
Semaphore ID = 196610
	      
  ターミナル2 (プロセス2実行)


% ./HanoiSemaphoreDisplay 15695884 196610

(塔の中身省略)
----   ----   ----
  A      B      C
Number of Moves: 597906885
Number of Disks: 30