PDF版

オペレーティング
システム
第7回
---並行プロセス
排他制御基礎
続き
---同期をとろうとすると
(排他制御をしようとすると)
デッドロックの可能性が生じる
---sleep&wakeupで
同期をとろうとして
デッドロックが生じる理由
↓
sleepすべきかどうかの判断と
sleep実行が非アトミック
---そこで
dijkstraによって
発明されたのが
---[[EM:セマフォ]]
構造体
(1)非負整数
(2)待ち行列
---プリミティブ
(基本命令)
down(&S) (P(S))
up(&S) (V(S))
Sはセマフォのこと
---down(&S)
[[PRE:
if Sの整数値が正 {
整数値を1減らす
実行継続
} else { /* Sの整数値が0 */
Sの待ち行列に登録
待ち状態に遷移
}
]]
---up(&S)
[[PRE:
if Sの待ち行列が空でない {
待ちプロセスを1つ実行可能状態へ遷移
upしたプロセスも実行可能状態へ遷移
} else {
Sの整数値を1増やす
実行継続
}
]]
---ポイント
down命令,up命令はアトミック
↓
TS命令を使って作る
---セマフォによる排他制御
Entry Sequence
↓
down(&S)
Exit Sequence
↓
up(&S)
Sの整数値の初期値は1
2以上にはならない
---Sの整数値が0か1のみ
↓
[[EM:バイナリセマフォ]]
≒
[[EM:ミューテックス]]
mutex
---mutexのプリミティブ
lock(mutex)
unlock(mutex)
---lock(mutex)
mutexをロック
すでにロックされていれば
後からロックしようとした
プロセスは待ち状態に遷移
---unlock(mutex)
mutexをアンロック
待ち状態のプロセスがあれば
1つを選んで/すべて
実行可能状態に遷移させる
---mutexの使い方
[[PRE:
lock(mutex)
クリティカルセクション
unlock(mutex)
]]
---実験してみよう
---[[PRE:
01: #include <stdio.h>
02: #include <stdlib.h>
03: #include <pthread.h>
04:
05: #define N 10000000
06:
07: pthread_mutex_t mutex;
08:
09: int n = 0;
10:
11: int read_data()
12: {
13: return n;
14: }
15:
16: void write_data(int x)
17: {
18: n = x;
19: }
20:
21: void *inc(void *param)
22: {
23: int i, x;
24:
25: puts("starting inc thread");
26: for (i = 0; i < N; i++) {
27:
pthread_mutex_lock(&mutex);
28:
x = read_data();
29:
x++;
30:
write_data(x);
31:
pthread_mutex_unlock(&mutex);
32: }
33: }
34:
35: void *dec(void *param)
36: {
37: int i, x;
38:
39: puts("starting dec thread");
40: for (i = 0; i < N; i++) {
41:
pthread_mutex_lock(&mutex);
42:
x = read_data();
43:
x--;
44:
write_data(x);
45:
pthread_mutex_unlock(&mutex);
46: }
47: }
48:
49: int main()
50: {
51: pthread_t it, ot;
52:
53: pthread_mutex_init(&mutex, NULL);
54: printf("n = %d\n", n);
55: pthread_create(&it, NULL, &inc, NULL);
56: pthread_create(&ot, NULL, &dec, NULL);
57: pthread_join(it, NULL);
58: pthread_join(ot, NULL);
59: printf("n = %d\n", n);
60: return 0;
61: }
]]
---セマフォによる同期
生産者・消費者問題
---[[PRE:
#define N 100
semaphore mutex = 1;
semaphore empty = N;
semaphore full = 0;
p01: void producer(void)
c01: void consumer(void)
p02: {
c02: {
p03: int item;
c03: int item;
p04: while (1) {
c04: while (1) {
p05:
item = produce_item(); c05:
down(&full);
p06:
down(&empty);
c06:
down(&mutex);
p07:
down(&mutex);
c07:
item = remove_item();
p08:
insert_item(item);
c08:
up(&mutex);
p09:
up(&mutex);
c09:
up(&empty);
p10:
up(&full);
c10:
consume_item(item);
p11: }
c11: }
p12: }
c12: }
]]
---前のプログラムをリライト
(1)c05-c07 → down(&count)
(2)countをfullにリネームしてセマフォ化
(3)セマフォempty=N-countを導入
(4)p06-p08 → down(&empty)
(5)c09-c10 → up(&empty)
(6)p10-p11 → up(&count)
(7)バッファの読み書きをmutexで保護
---セマフォの問題点
↓
間違えやすい
---間違ったプログラム
[[PRE:
#define N 100
semaphore mutex = 1;
semaphore empty = N;
semaphore full = 0;
p01: void producer(void)
c01: void consumer(void)
p02: {
c02: {
p03: int item;
c03: int item;
p04: while (1) {
c04: while (1) {
p05:
item = produce_item(); c05:
down(&full);
p06:
down(&mutex);
c06:
down(&mutex);
p07:
down(&empty);
c07:
item = remove_item();
p08:
insert_item(item);
c08:
up(&mutex);
p09:
up(&mutex);
c09:
up(&empty);
p10:
up(&full);
c10:
consume_item(item);
p11: }
c11: }
p12: }
c12: }
]]
---正しいプログラム
[[PRE:
#define N 100
semaphore mutex = 1;
semaphore empty = N;
semaphore full = 0;
p01: void producer(void)
c01: void consumer(void)
p02: {
c02: {
p03: int item;
c03: int item;
p04: while (1) {
c04: while (1) {
p05:
item = produce_item(); c05:
down(&full);
p06:
down(&empty);
c06:
down(&mutex);
p07:
down(&mutex);
c07:
item = remove_item();
p08:
insert_item(item);
c08:
up(&mutex);
p09:
up(&mutex);
c09:
up(&empty);
p10:
up(&full);
c10:
consume_item(item);
p11: }
c11: }
p12: }
c12: }
]]
---producerの
2つのdown命令の
順番が入れ替わっている
---もし
producerが
down(&mutex)に成功し,
down(&empty)で待ち状態に
なったら
---consumerが
down(&mutex)で
待ち状態になる
↓
デッドロック