Operating Systems. Exercise 07

I. プロセス間でのメモリ共有 (準備課題, 配点無し)
演習問題を解くために必要なプロセス間メモリ共有のためのC言語インターフェースを説明します。


メモリ確保についての復習

Cプログラム内で変数を宣言すると, それを格納するためのメモリ領域が確保されます。 今までは以下のような確保のされかたでした。

静的領域確保の場合


int nVariable;        //変数1つ
int naStaticArray[2]; //変数配列
	
動的領域確保の場合

int *nVariable; //ポインタ変数
nVariable = (int*)malloc(1*sizeof(int)); //領域割り当て(変数1つ)

int *naDynamicArray; //ポインタ変数
naDynamicArray = (int*)malloc(2*sizeof(int)); //領域割り当て(変数配列)
	
これらの確保の仕方の場合, ある"プロセス1"から他の"プロセス2"にデータを渡すには パイプによって標準入出力を経由したり,
"プロセス1"から外部ファイルに書き出した後 "プロセス2"から読み込むなど, 一度別の何かを経由するような方法が必要でした。
一方, 共有メモリとは 別々の"プロセス1"と"プロセス2"から 直接 同じ変数の領域にアクセスする機構です。


共有メモリ領域の確保と 領域の変数への割り当て

C言語では共有メモリ確保や領域割り当てなどの操作を, shm*系システムコールによって実現できます。
以下のプログラムで、確保の仕方など確認しプログラムの意味を理解して演習問題に備えてください。

ステップ1. 共有メモリ領域を確保し, 領域を変数に割り当てる

ShareMemTest.h


//共有メモリ領域の確保先変数nVar1とnVar2
int *nVar1;
int *nVar2;

//共有メモリ領域のサイズはnVar1とnVar2の分で型はint
int nShareMemSize = 2*sizeof(int);
	
ShareMemTest1.c

#include "ShareMemTest.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int nShareMemID;

int main() {

  //共有メモリ領域確保を要求し IDを覚えておく
  nShareMemID = shmget(IPC_PRIVATE, nShareMemSize, SHM_R|SHM_W);
  printf("nShareMemID = %d\n", nShareMemID);

  //ポインタ変数nVar1にshmatで共有メモリ領域を割り当てる
  nVar1 = shmat(nShareMemID, 0, SHM_R|SHM_W);

  //ポインタ変数nVar2のための領域は, nVar1のアドレスから1つ先にあるのでそこを指す
  nVar2 = nVar1 + 1;

  //nVar1とnVar2に共有メモリ領域を割り当てたので 使えるようになる
  *nVar1 = 1;
  *nVar2 = 2;
  printf("process1 : nVar1 = %d\nnVar2 = %d\n", *nVar1, *nVar2);

  //別ターミナルからipcsを実行し共有メモリ領域が確保されたのを確認するために 終わるのを遅らせる
  sleep(20);

  printf("process1 : nVar1 = %d\nnVar2 = %d\n", *nVar1, *nVar2);

  //共有メモリ領域開放
  shmctl(nShareMemID, IPC_RMID, 0);

  return 0;

}
	

ターミナル1 (ShareMemTest1実行)

(上のプログラムの実行形式をShareMemTest1とする)
% ./ShareMemTest1 

nShareMemID = 12353572
nVar1 = 1, nVar2 = 2

(sleepで20秒待っている間に, 別のターミナル2でipcsを実行し
 共有メモリが確保されていることを確認してください)

nVar1 = 1, nVar2 = 2
	      
ターミナル2 (ipcs実行)

% ipcs -b

T        ID      KEY        MODE        OWNER    GROUP      SEGSZ
Shared Memory:
m  12353572        0    --rw-------     user     group       8

(共有メモリID12353572で, int型2つ分なので8バイト分の領域が取られています)
	      

ステップ2. 別プロセス間で共有メモリを読み書きする

別プロセスを実行するためのプログラムを用意します (ShareMemTest2.c)

#include "ShareMemTest.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int nShareMemID;

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

  if (argc != 2) return 0;

  //共有メモリIDを引数から取る
  nShareMemID = atoi(argv[1]);
  printf("nShareMemID = %d\n", nShareMemID);

  //共有メモリIDから共有メモリ領域を得る
  nVar1 = shmat(nShareMemID, 0, SHM_R|SHM_W);
  nVar2 = nVar1 + 1;

  printf("process2 : nVar1 = %d, nVar2 = %d\n", *nVar1, *nVar2);

  //nVar1の中身を2に書き換えてみる
  *nVar1 = 2;
  printf("process2 : nVar1 = %d, nVar2 = %d\n", *nVar1, *nVar2);

  return 0;

}
	

ターミナル1 (ShareMemTest1実行)

% ./ShareMemTest1 

nShareMemID = 12746786
process1 : nVar1 = 1, nVar2 = 2
[sleepで20秒待っている間に, 別のターミナル2で別プロセスを実行]

process1 : nVar1 = 2, nVar2 = 2
[別プロセスによって共有メモリに確保された変数の中身が書き換わっている]
	      
ターミナル2 (ShareMemTest2実行)

% ./ShareMemTest2 12746786
[共有メモリIDをコマンドラインから渡す]

nShareMemID = 12746786
process2 : nVar1 = 1, nVar2 = 2
process2 : nVar1 = 2, nVar2 = 2
	      


II. プロセス間メモリ共有(配点100%)

課題1

課題: 棒の状態(naA,naB,naC)と円盤数(nDisks)と総移動回数(nMoves)を格納する変数を共有メモリ領域に確保し,
独立したプロセス1(ハノイの塔を解く)とプロセス2(塔の内容を表示)で役割分担する プログラムを作成してください。

共通ヘッダ : HanoiShareMem.h ヘッダ
プロセス1 : ハノイの塔の円盤移動(Solve側) テンプレート
プロセス2 : ハノイの塔の状態を出力する(Display側) テンプレート

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

% ./HanoiShareMemSolve 30

Share Memory ID = 16089130
(ハノイの塔の円盤移動が行われる)
Number of Moves: 1073741823
Number of Disks: 30
	      
  ターミナル2 (プロセス2実行)

% ./HanoiShareMemDisplay 16089130

[途中経過が出力される(下の解説を読んでください)]

Number of Moves: 1063376533
Number of Disks: 30
Move: 1063376533
(省略)
----   ----   ----
  A      B      C

	      

解答の結果出力される途中経過について

途中経過を出力すると以下のように, 同じ円盤が複数存在するなど何かおかしなものになります。


(一部略)
| 0|   | 0|   | 0|
| 0|   | 0|   | 0|
| 0|   | 0|   | 0|
| 0|   | 0|   | 1|
| 0|   | 3|   | 2|
| 0|   | 2|   | 0|
| 3|   | 0|   | 0|
| 0|   | 8|   | 3|
| 7|   | 9|   | 8|
|10|   |12|   |13|
|11|   |15|   |18|
|14|   |16|   |21|
|17|   |19|   |22|
|24|   |20|   |25|
|27|   |23|   |26|
|28|   |30|   |29|
----   ----   ----
  A      B      C
	

これは, Solve側のプロセスから共有メモリに書き込む処理を完全に終えないうちに,
Display側のプロセスが共有メモリから読み込むことによって起こる, 資源共有では良く知られた有名な問題です。
よって, 今回はこのおかしな出力が解答になります

次回の演習で, これへの対処方法(排他的共有メモリアクセス制御)の実験をします。