Operating Systems. Exercise 10
I. メッセージキュー (準備課題, 配点無し)
プロセス間通信のための仕組みにはメッセージキューというものもあります。
送信側はデータブロック(パケット)をMessage Queueに格納し,
受信側はMessage Queueに格納された順番にパケットを取り出し利用するというものです
例として "MsgQueueSend.c" がQueueにデータを送信し,
"MsgQueueReceive.c" がQueueからデータを取り出すプログラムとします。
送信側はMyMsgPacket型の構造体(自分で型を宣言する)を送信し受信側はそれを受信します
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
//キューIDを生成するユニークキー
#define MY_MESG_KEY 1234L
//メッセージの読み書きパーミッション
#define MY_MESG_PERMISSION 0666
//メッセージパケット型
typedef struct {
//---- メッセージパケットの最初の変数は使っても使わなくても必ずlong intにする ----
long int nMessageType;
//---- それ以降の変数は任意に宣言できる ----
char sMessage[32];
} MyMessagePacket;
マクロ"MY_MESG_KEY"は"Queue IDを生成するユニークキー"です。新しくMessage Queueを作りたい場合既に作成されているユニークキーと
被らないものを指定してください。 既に作成されたキーからメッセージパケットを取り出したい場合は同じユニークキーを使います。
"MyMessagePacket(メッセージパケット型)" は通信に使うためのデータをまとめた構造体で,
自分で型を宣言します
メッセージパケット型の最初の変数は使っても使わなくても必ずlong intにします
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "MsgQueue.h"
void SendMessage(int);
int main(int argc, char *argv[]) {
int i;
//ユニークキーMY_MESG_KEYからメッセージキューを作成
int nMesgQueueID = msgget(MY_MESG_KEY, MY_MESG_PERMISSION|IPC_CREAT);
if (nMesgQueueID < 0) {
fprintf(stderr, "ERR:msgget\n");
exit(1);
}
//メッセージキューにメッセージ送信
for (i=0; i<1; ++i) {
SendMessage(nMesgQueueID);
sleep(10);
}
//メッセージキュー削除
if (msgctl(nMesgQueueID, IPC_RMID, NULL) < 0) {
fprintf(stderr, "Can't RMID queue?\n");
exit(1);
}
return 0;
}
void SendMessage(int nMesgQueueID) {
MyMessagePacket mesgPacket;
//パケットの中身を作成
mesgPacket.nMessageType = 1; //--- 使わなくても1以上の値に初期化はする必要がある ---
strncpy(mesgPacket.sMessage, "string", 7);
//送信メッセージの大きさは MyMessagePacket型の最初のlong intを除いたサイズ分を指定する
int nMesgLength = sizeof(mesgPacket) - sizeof(mesgPacket.nMessageType);
//メッセージ送信
if (msgsnd(nMesgQueueID, &mesgPacket, nMesgLength, IPC_NOWAIT) < 0) {
fprintf(stderr, "ERR:msgsnd\n");
}
}
MsgQueueReceive.c
(メッセージ受信側)
#include <stdio.h>
#include <stdlib.h>
#include "MsgQueue.h"
int ReceiveMessage(int nMesgQueueID);
int main(int argc, char *argv[]) {
int nMesgQueueID = msgget(MY_MESG_KEY, 0);
if (nMesgQueueID < 0) {
fprintf(stderr, "ERR:msgget\n");
exit(1);
}
while (ReceiveMessage(nMesgQueueID) == 0) {
//メッセージキューが削除されていないか確認する
nMesgQueueID = msgget(MY_MESG_KEY, 0);
if (nMesgQueueID < 0) {
fprintf(stderr, "Queue has been removed\n");
exit(1);
}
}
return 0;
}
int ReceiveMessage(int nMesgQueueID) {
MyMessagePacket mesgPacket;
int nMesgLength = sizeof(mesgPacket) - sizeof(mesgPacket.nMessageType);
nMesgLength = msgrcv(nMesgQueueID, &mesgPacket,
nMesgLength, 0, MSG_NOERROR);
printf("message length : %d\n", nMesgLength);
if (nMesgLength < 0) {
fprintf(stderr, "ERR;msgrcv\n");
return 0;
}
printf("sMessage : %s\n", mesgPacket.sMessage);
return nMesgLength;
}
ターミナル1 (MsgQueueSend実行)
|
ターミナル2 (MsgQueueReceive実行)
|
ターミナル1 (MsgQueueSend実行)
|
ターミナル2 (MsgQueueReceive実行)
|
アラームを設定することによって、一定時間後にシグナルを自動送信することが出来ます
以下のプログラムでいろいろ実験してください。
アラームによるシグナル送信プログラム (AlarmTest)
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
//プロトタイプ宣言を適宜
int nSignalReceived = 0;
int nSignalPeriod = 5; //秒
int main(int argc, char *argv[]){
int i;
//秒指定のアラーム設定
alarm(nSignalPeriod);
//アラームシグナルのハンドラ設定
signal(SIGALRM, SignalHandler);
for (i=0; i<30; ++i) {
sleep(1); //すごい勢いで出力され重くなるので わざと1秒ずつ待っています
printf("%d\n", i);
if (nSignalReceived) {
printf("signal received\n");
nSignalReceived = 0;
}
}
return 0;
}
void SignalHandler(int code) {
nSignalReceived = 1;
//アラーム再設定
alarm(nSignalPeriod);
}
実行
% ./AlarmTest
結果
0
1
2
3
4
signal received
5
6
.
.
II. メッセージキューによるプロセス間通信(配点100%)
課題
ハノイの塔の状態を格納する構造体などを宣言したヘッダファイルが与えられています(以下参照)
ハノイの塔を解きながら一定時間ごとにメッセージキューに塔データを送信するプロセス1と,
メッセージキューから塔データを取り出し塔の状態を出力するプロセス2を担当する
プログラムを作成してください。
ヘッダ : HanoiMsgQueue.h
ハノイの塔の円盤移動(Solve)
(テンプレート)
ハノイの塔の状態を出力する(Display)
(テンプレート)
例:
ターミナル1 (プロセス1実行)
|
ターミナル2 (プロセス2実行)
|