Operating Systems. Exercise 09
OSは複数ユーザを受け入れたり 複数のプロセスを同時に実行します。
リソースへの同時アクセスに対するリスクが常在するため, OSは一般に排他制御を行うための機構を備えています。
現在ではSemaphore(セマフォ)やMutex(ミューテックス)と呼ばれる機構が標準的に使われており,
共有メモリでの排他制御にはこれらの仕組みを利用することもできます。
セマフォは共有メモリなどの共有領域に確保されたカウンタとして実装されています。
カウンタの初期値は同時アクセスを許すプロセス数で, クリティカルな処理部分へ入るとカウンタが-1され,
その処理が終了すると+1される機構です。
カウンタが0のときは, 既に同時アクセスを許すプロセス数に達しているので,
資源を使用中の他プロセスが処理を終えてカウンタを加算してセマフォが0より大きくなるのを待ちます
プログラム内
|
一般にMutexは同時アクセスを許すプロセス数が1のSemaphoreと考えられます。
アクセス制御の対象となる部分へは1つのプロセスしか同時にアクセスできません。
同時アクセスを許すプロセス数を1にしたSemaphore(Mutex)のテストです
#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
#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(¤tTime);
printf("%s:start time: %s", sProcessName, (char*)ctime(¤tTime));
fflush(stdout);
//ある程度時間がかかる処理を模している
sleep(1);
time(¤tTime);
printf("%s:end time: %s", sProcessName, (char*)ctime(¤tTime));
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);
}
上のソースコードでは, セマフォを実際に使用する部分をコメントアウトしてあります
% ./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()のコメントアウトを親と子両方で外し,
% ./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実行)
|
ターミナル2 (プロセス2実行)
|