並行システム
システム情報系情報工学域,
システム情報工学研究科コンピュータサイエンス専攻
新城 靖
<yas@cs.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2019/2019-08-09
あるいは、次のページから手繰っていくこともできます。
http://www.cs.tsukuba.ac.jp/~yas/cs/
http://www.cs.tsukuba.ac.jp/~yas/
- 期末試験は行いません。期末レポートもありません。毎回のレポートで成
績を付けます。
There is no final exam and no final report.
Grade are determined by individual class reports.
- レポート出していない人は Web ページから出してください。
You must submit reports via the Web page.
- レポートは、8月17日 までに全部提出してください。
You must submit all the reports by August 17.
- いくつかのレポートは再提出の必要があるかもしれません。
You may have some reports to post again.
- トランザクション(transaction)、begin_transaction、commit、abort
- Argus
- 投機的実行 speculative execution
相互排除、デッドロックより高いレベルの抽象が欲しい。
We need better abstracts than mutual exclusion and dead locks.
ロックよりも楽にプログラムを作りたい。
We would like to write programs more easily than that with locks.
特に分散では。集中でも、フォールト・トレランスの実現では必要。
Especially in distributed systems.
Transaction is essential to realize fault tolerance.
商談は、成立するか、しないか。
all or nothing。
歴史:1960年代、テープの時代。
失敗したら、巻き戻す(rollback)。
銀行口座間の預金の移動。
- 口座1から出金
- 口座2に入金
- Begin_Transaction
- Commit (End Transaction)
- Abort
- (Read)
- (Write)
- Atomic
- 外の世界からみると1つ。中間状態が見えない。不可分。
- Consistency
- 不変量(invariant)を保つ。例:口座間の預金の移動。預金の総額は不変。
- Isolated
- 並行するトランザクションは、孤立している。直列化可能(serializable)
- Durable
- Commit すると永続的。
注意:Java の serializable とは意味が全く違う。
複数のトランザクションは、並行に実行可能。しかし、その複数のトランザク
ションの最終結果は、それらを「ある順序」で「逐次的に」実行した時と同じ結果
になる。
逆に、トランザクションは、直列化可能なスケジュールが存在するなら、並行
に実行してもよい。直列化可能でないなら、トランザクションは、開始を遅ら
せるか、アボートする。

図? トランザクションの処理(並行実行)/Concurrent execution of transactions

図? トランザクションの処理(直列化の例その1)/A serialized execution of transaction No.1

図? トランザクションの処理(直列化の例その2)/A serialized execution of transaction No.2

図? トランザクションの処理(直列化の例その3)/A serialized execution of transaction No.3
航空券の乗り継ぎの予約のプログラムの例。
成田からサンフランシスコ経由でオーランドまで行きたい。
Begin_Transaction();
reserve("NRT-SFO");
reserve("SFO-MCO");
Commit();
プログラムの記述としては、これだけ。どこにもロックは出てこない。内部的
には、ロックは行われる(ことがある)。
途中の reserve() が失敗したら、トランザクション全体を abort() する。
目的
親が abort されたら、子供が commit したものも無効になる。
- プライベート作業空間。書き換える前にコピーを作り、コピーを更新。
Private work space.
- 先行書き込みログ。更新作業をログに記録して、後で戻せる(rollback)
ようにする。
Write Ahead Logging (WAL).
全てのデータをコピーすると重たい。
- 読み込みではコピーしない
- ファイル全体だけではなく、インデックス(inode)だけをコピーする。シャドー・ブロック。

図? ファイルの変更されたブロックだけをコピー)
実行予定リスト(intentions list)
シャドー・コピーを作らず、ファイルのその場所を書き換える。
その前に、次の情報を安定記憶にログとして書き込む。
コミットされると、何もしない。
アボートされると、ログを後ろから前に読み込み undo する。
クラッシュからの回復もできる。
- RAM
- 電源が落ちると消える
- ディスク
- ディスクヘッドのクラッシュで消える
- 安定記憶
- 洪水や地震でも生き残るように設計されている。
安定記憶の実現方法:2個のディスクの組みを使う(図?(a))。

図? 安定記憶の実現
更新時:
- ディスク1のあるセクタに書き込む。
- ディスク1からそのセクタを読み込む。
- 内容を比較する。同じなら、ディスク2の同じ位置に書き込む。
3 の前にクラッシュしても、常にディスク1が正しい(図?(b))。
この場合は、ディスク1からディスク1へコピーする。
チェックサムエラーが起きた場合(図?(c))、他のディスクからコピーして回復する。
できるだけ効率のよい、直列化可能なスケジューリングを求める。
- ロッキング
- 楽観的並行性制御
- タイムスタンプ
- 2相コミット
もっとも単純な方法:
- Begin Transaction で、ファイルをロックする。
- Commit/Abort でアンロックする。
注意:プログラマは、ロックを意識しない。
制約が強すぎる。
読み取りロック:read lock と write lock を分ける。
ロックの粒度(granularity)が大事
- 小さくする
- 並列性が高まる。ロック自体のオーバヘッドが増える。
デッドロックが起こりやすくなる。
- 大きくする
- 逆
デッドロックが起きにくい、直列化可能な方法。
- 第1相(growing phase)
- ロックの数が増える一方
- 第2相
- ロックの数が減る一方

図? 2相ロック/Two-phase locking
定理:トランザクションの実現で2相ロックを使うならば、インターリーブさ
れることで作られるスケジュールは直列化可能である。
実際のシステムでは、Commit/Abort するまで、一度確保したロックをはなさない。
第2相で、まとめて全部ロックを解放する(strict two-phase locking)。

図? 2相ロック/Strict two-phase locking
利点
- 中間的な状態を外から見られることがない。無駄な計算が起きない。
- 連鎖アボート(cascaded abort)が起きない
- デッドロックの検出に時間切れが使えることがある。
2相ロックでも、デッドロックは起きる。
- ロックの順番を決める
- グラフのループを検出する
- 時間切れ
注意:2相ロックと2相コミットは別。
どんどんやって、後でまずかったことがわかった時点で abort する。
衝突が稀である時に有効。
実現(衝突時の対応):プライベート作業空間を使う実現が便利。
どんどんプライベート作業空間上のファイルを更新して、最後にコミットする
か削除する。
性質
- デッドロックがおきない。
- 並列性が高い。
- 衝突して失敗する確率が高くなると辛い。
操作にタイムスタンプを付けて、まずいものをアボートする。
アルゴリズム:
- ファイルには、読み取りのタイムスタンプと書き込みのタイムスタンプが付け
られる。
- あるプロセスがファイルをアクセスする時、
トランザクションのタイムスタンプとファイルのタイムスタンプを比較する。
ファイルの方が古ければ、問題なし。
- ファイルの方が新しければ、トランザクションをアボートする。
楽観的:同じファイルを独立したトランザクションで同時にアクセスすること
は、あまりない。
Lamport のアルゴリズム等でクロックが同期されていれば、分散システムでも
使える。
2相コミット(two-phase commit)は、分散システムにおけるトランザクション
の実装方法。(注意: 2相ロック(two-phase lock)ではない)。
2種類のプロセス
- コーディネータ・プロセス、1つ
- サブオーディネート(残りの関連するプロセス)。従属。
2つの相
- 第1相
- 全プロセスを Commit も Abort もできる状態にする。
- コーディネータは、ログに「準備」と書く。
- コーディネータは、サブオーディネートに「準備」メッセージを送る。
- サブオーディネートは、ログに「準備完了」と書く。
- サブオーディネートは、コーディネータに「準備完了」メッセージを送る。
- コーディネータは、全てのサブオーディネートからの応答を待つ。
- 第2相
- 実際に Commit する。
- コーディネータは、ログに「コミット」と書く。
- コーディネータは、サブオーディネートに「コミット」メッセージを送る。
- サブオーディネートは、ログに「コミット完了」と書く。
- サブオーディネートは、コーディネータに「コミット完了」メッセージを送る。
- コーディネータは、全てのサブオーディネートからの応答を待つ。
クラッシュした時には、ログから回復させる。
https://www.slideshare.net/kumagi/ss-78765920,分散システムについて語らせてくれ by 熊崎宏樹
- p32, 2 Phase Commitはバグっている
ANSI/ISO SQL 標準で定義。Serializable は、重いので、多くの DBMS ではデ
フォルトでは無効。
分離レベル | ダーティーリード | ノンリピータブル・リード | ファントム・リード |
Read Uncommitted | 有 | 有 | 有 |
Read Committed | 無 | 有 | 有 |
Repeatable Read | 無 | 無 | 有 |
Serializable | 無 | 無 | 無 |
- ダーティー・リード。コミット前のデータが読める。
- ノンリピータブル・リード。
他のトランザクションが先に終了すると、途中で値が変わったように見える。
- ファントム・リード。関係データベースで、他のトランザクションて行が
追加され、コミットされた時、select の range が変わってしまって見えてし
まう。
トランザクション機能を内部に含む言語。
発音は、英語読みでアーガス。ギリシャ神話読みでは、アルゴス。
Barbara Liskov: "Distributed programming in Argus", Communications of
the ACM, Vol.31, No.3, pp.300-312, 1988.
https://dl.acm.org/citation.cfm?id=42399
文法は、CLU 言語に似ている。
参考:
並行に動作するオブジェクト(現在の用語ではスレッド)を実現する。
- ハンドラ(handler)と呼ばれる
他のガーディアンから呼ぶことができる手続き(エントリ)を持つ。
- 初期化用にクリエータ(creator)と呼ばれるエントリを持つ。
ガーディアンは、2種類の局所変数を持つ。
- 安定(stable)
- クラッシュ(再起動)しても、生き残る。
- 揮発(volatile)
- クラッシュ(再起動)すると、値が失われる。
安定な変数に対するトランザクションを実現する。安定な変数を、整合性がと
れた状態から、次の整合性がとれた状態へ遷移させる。(
途中でクラッシュしたら、元に戻す。)
- トップアクション
- トップレベルのトランザクションを実現する。commit するか abort するか。
内部にサブアクションを持つ。
abort したら、内部のサブアクションもすべて abort。
- サブアクション
- ネストしたトランザクションを実現する。
他のガーディアンのハンドラの呼出し(遠隔手続き呼び出し)は、
サブアクションになる。
- 2相コミット
- 書込みロックで、作業用のコピーが作られる。
- 名前 Name
- 生成ルーチン。引数の取得。Routines
- 安定な変数。Stable variables
- 揮発的な変数。Volatile variables
- ハンドラ手続き(外から呼ばれるエントリ)。Handlers
- バックグランド・ルーチン。ガーディアン自身のプログラム。
Backgroud routines.
- 復旧手続き。クラッシュ後に実行され、揮発的な変数を状態に戻す。
Recovery procedures.
安定な変数については、システムが自動的に整合性が取れた状態に復旧する。
1: branch = guardian is create handles total, open, close, deposit, withdraw
2: % type definitions
3: htable = atomic_array[bucket]
4: bucket = atomic_array[pair]
5: pair = atomic_record[num: account_number, acct: acct_info]
6: acct_info = atomic_record[bal: int]
7: account_number = atomic_record[code: string, num: int]
8: intcell = atomic_record[val: int]
9:
10: stable ht: htable % the table of accounts
11: stable code: string % the code for the branch
12: stable seed: intcell % the seed for generating new account numbers
13:
14: create = creator (c: string, size: int) returns (branch)
15: code := c
16: seed.val := 0
17: ht := htable$new()
18: for i: int in int$from_to(1, size) do
19: htable$addh(ht, bucket$new())
20: end
21: return (self)
22: end create
23:
24: total = handler () returns (int)
25: sum: int := 0
26: for b: bucket in htable$elements(ht) do
27: for p: pair in bucket$elements(b) do
28: sum := sum + p.acct.bal
29: end
30: end
31: return (sum)
32: end total
33:
34: open = handler () returns (account_number)
35: intcell$write_lock(seed) % get a write lock on the seed
36: a: account_number := account_number${code: code, num: seed.val}
37: seed.val := seed.val + 1
38: bucket$addh(ht[hash(a.num)], pair${num: a, acct: acct_info${bal: 0}})
39: return (a)
40: end open
41:
42: close = handler (a: account_number) signals (no_such_acct, positive_balance)
43: b: bucket := ht[hash(a.num)]
44: for i: int in bucket$indexes(b) do
45: if b[i].num != a then continue end
46: if b[i].acct.bal > 0 then signal positive-balance end
47: b[i] := bucket$top(b) % store topmost element in place of closed account
48: bucket$remh(b) % discard topmost element
49: return
50: end
51: signal no_such_acct
52: end close
53:
54: lookup = proc (a: account_number) returns (acct_info) signals (no_such__acct)
55: for p: pair in bucket$elements(ht[hash(a.num)]) do
56: if p.num = a then return (p.acct) end
57: end
58: signal no_such_acct
59: end lookup
60:
61: deposit = handler (a: account_number, amt: int) signals (no_such_acct,
62: negativ_amount)
63: if amt < 0 then signal negative_amount end
64: ainfo: acct_info := lookup(a) resignal no_such_acct
65: ainfo.bal := ainfo.bal + amt
66: end deposit
67:
68: withdraw = handler (a: account_number, amt: int) signals (no_such_acct,
69: negative_amount, insufficient_funds)
70: if amt < 0 then signal negative amount end
71: ainfo: acct_info := lookup(a) resignal no_such_acct
72: if ainfo.bal < amt then signal insufficient_funds end
73: ainfo.bal := ainfo.bal - amt
74: end withdraw
75: end branch
- ガーディアンを定義し、branch という変数にバインド
- クリエータは、create()
- ハンドラは、total(), open(), close(), deposit(), withdraw()
- 型定義。record は、構造体の定義。
- atomic なので、システムによりトランザクションの対象となる。
- 変数定義。stable なので、全部安定。
揮発的な変数がないので、クラッシュからの回復コードはない。
- バックグランドの手続きもない。
- seed は、口座番号をユニークにするためのカウンタ。
Etherium は、ブロックチェーン技術の一種。
- アカウントは、address を持つ。
- アカウントは、次のような状態を持つ。
- アカウントに、2種類ある。
- EOA(Externally Owned Account)。個人の財布。人についている。
公開鍵・秘密鍵の組を持つ。
- Contract Account。Smart contract。ブロックチェーンに付随した仮想計
算機で実行されるプログラム。Solidity 言語や Serpent 言語で書く。
Etherium Virtual Machine (EVM) のコードを持つ。状態を持つ。ブロックチェー
ン・ネットワークにディプロイされる。
- EOA から他の EOA、または、EOA から Contract Account に
ether 値を送信することを Etherium では、
トランザクションと言っている。
トランザクションは、次のデータを含む。
- to: 受信者のアドレス
- data: ペイロード
- value: 送信される ether 値
- nonce: 送信者が今まで送信したトランザクションの数
- gasprice, gasLimit, starttagas: 手数料関連のパラメタ
- (v,r,s): ディジタル署名
Contract Accountから発信されるトランザクションは、メッセージと呼ばれる。
- トランザクションで、二重支払いが起きないように、ether が増えたり減っ
たりしないように、ブロックチェーンのノード(マイナー)が監視している。
- トランザクションの実行には、手数料(gas)がいる。
- Contract の関数の実行には、トランザクションと呼び出しの二種類があ
る。
- トランザクションでは、contract に、ether をトランザクションで送り、
Contract 内部の状態を変化させることができる。状態の変化は、ブロックチェー
ンに記録される。
- 呼び出しでは、contract の状態を変化させられない。トランザクション
ではないので、(軽いものは)手数料が不用。呼び出しは、ブロックチェーンか
らContract の状態を集めて、コードを、ローカルで実行するということでも良
い。
contract Bank {
mapping(address => uint256) public accounts;
function deposit() {
accounts[msg.sender] += msg.value;
}
function withdraw(uint256 amount) {
if(amount > accounts[msg.sender])
throw;
accounts[msg.sender] -= amount;
if(!msg.sender.send(amount))
throw;
}
function get_balance() {
return accounts[msg.sender];
}
}
- mapping は、ハッシュ表のようなもの。この例では、キーが address で、
値が uint256。変数名が accounts。address は、アカウントのアドレス。
- msg は、トランザクションで送らてきたメッセージ。
次のようなフィールドがある。
- msg.sender: 送信者のアドレス。
- msg.value: 送られてきた ether の値。
deposit() は、送信元のアカウントから amount 分のether を受け取る。
- 受け取った ether 値だけ、accounts[msg.sender] を増やす。
- このトランザクションで、contract が持っている balance も、増える。
withdraw() は、メッセージの送信元に、預かっている ether を amount 分だけ返す。
- require() が成り立たないと、トランザクションは、abort される。
- まず、accounts[msg.sender] を減らす。先に減らすのが大事。
- 次に、msg.sender にトランザクションで amount 分だけ ether を送る。
- このトランザクションで、contract が持っている balance が減る。
残高を確認し、先に accounts[msg.sender] を減らしてからトランザクション
を行わないと、複数同時に withdraw() が呼ばれた時に、問題が生じる。
get_balance() は、残高照会。
状態を変えないので、トランザクションではなく call で実行できる。
投機的実行(speculative execution)、将来利用されるか利用されないか確定さ
れる前に実行を開始してしまうこと。楽観的実行(optimistic execution)とも言う。
if( a() ) {
b();
}
else {
c();
}
- 普通は、a() が true か false かを返すのを待ってから
b() 、または、c() を開始する。
- 投機的処理では、a() の完了を待たずに、先に、b()、または、c() また
は、b() と c() の両方を実行する。

図? 投機的実行 Speculative execution
この例では、a() が完了した時には、b()、c() が終わっていた時には、見かけ
上、即座に終わる。
- 現在のCPUは、機械語命令の単位で投機的実行を行っている。
機械語命令やデータのプリフェッチに特に有効。
- 最近のCPUは、分岐予測をして、条件分岐命令の先まで先に実行する。条
件が満たされない時には結果は捨てられる。
- 関数単位、手続き単位の投機的実行は、CPU が遊んでいる時には有効。た
だし、省電力とは相反する。
- 関数単位、手続き単位の投機的実行は、通信遅延の隠ぺいに使える。
- then 部や else 部の処理に、副作用がない(関数型)の時には、
実装は楽。
- 副作用がある時には、トランザクション的な実装が求められる。
C. Cowan and H. L. Lutfiyya: "A wait-free algorithm for optimistic
programming: Hope realized", In Proceedings of the 16th International
Conference on Distributed Computing Systems(ICDCS), pages 484-493,
1996.
RPCの遅延を隠す。
This paper proposes hiding RPC delays by using optimistic execution.
新城の研究成果。
- 根路銘, 當眞, 新城, 喜屋武, 翁長: "粗粒度投機的並列処理を支援
するオペレーティング・システムの構想", SWoPP'94, 情報処理学会研究会報
告94-ARC-107-12, Vol.94,No.66, pp.89-96 (1994年7月).
- Jun Sun, Yasushi Shinjo and Kozo Itano: "The Implementation of a
Distributed File System Supporting the Parallel World Model", The
Third International Workshop on Advanced Parallel Processing
Technologies, pp.43-47 (Oct.19-21, 1999).
- 石井 孝衛, 新城 靖, 板野 肯三: "プロセストレース機能を用いた世界
OSの実現",情報処理学会論文誌, Vol.43, No.6, pp.1702-1714 (2002年6月).
- 今里邦夫, 鈴木真一, 新城靖, 板野肯三: "動的リンクとパケットフィル
タを用いた世界OS のネットワーク機能の実現",日本ソフトウェア科学会第6回
プログラミングおよび応用のシステムに関するワークショップ(SPA 2004), ポ
スターセッション (2004年3月1-3日).
- Yasushi Shinjo, Wataru Ishida and Jinpeng Wei: "Implementing a
parallel world model using Linux containers for efficient system
administration", IEEE Second International Workshop on Container
Technologies and Container Clouds (WoC 2018), 7 pages (2018).
SF (Science Fiction) のパラレルワールド(parallel world, parallel
universe)を、オペレーティング・システムのレベルで実現する。

図 SFのパラレルワールド
SFでは、時々別の世界にジャンプしたり人が入れ替わったりする。
Undo とは、do の効果を打ち消して、もともと do しなかったことにする。
(do not とは違う。)
世界OSでは、ファイルの内容を変更すると、2つの世界ができる。

図 世界OSで使える世界の操作
実際には、先に世界を作り、その中でファイルの内容を書き換える。うまくいっ
たら、融合する。失敗すれば削除して、変更の結果を無効にする。
普通の仮想計算機では、融合はできない。継承のコストが大きい。
- クラックキング、攻撃による破壊からの迅速な回復。攻撃される
可能性があるプログラム、
怪しいプログラムを子世界で実行して、破壊されたら削除して回復させる。
Fast recovering after attack.
- 投機的処理/楽観的処理。実行しろと命じられる前に先行的に実行する。
投機的(並列)make。
Speculative execution. Speculative (parallel) "make".
- 「差分」を first class object として扱う。
複数のPCでのファイル内容の統一。複数の世界の内容を比較して
更新された部分を反映させる。
A difference as a first class object.
以下の *全て* の問いについて答えなさい。
Answer *all* of the following questions.
次の内容を含む「テキスト」ファイルを作成し、
レポート提出ページ
から提出しなさい。
締切りは、2019年8月13日、 23:59:59 とする。
2つの銀行口座の間で送金するプログラムの「概略」を、トランザクションを使っ
て簡単に書きなさい。基本となる言語としては、C言語、Java言語、Ruby言語、
または、それに類するものを使いなさい。トランザクションを実装するための
ライブラリ関数が利用可能であるものとする。そのようなライブラリ関数には、
トランザクションを実現するための命令
と類似の名前をつけなさい。
なお、この問題では、概略を示せば十分であり、変数宣言の欠如や型の不一致
等のコンパイラが検出するような誤りは、減点しない。
Write a sketch of a program that transfers money between two bank accounts.
You may use the syntax of the C, Java, or Ruby language.
You can use the library functions that implement transactions.
These library functions must have good names that are similar to
the operations to realize transaction.
You do not have to write a runnable program in this question.
You do not have to fix compilation errors, such as no variable declaration and type incompatibility.
問題(1001) で、ロックを使う方法と比較して、トラン
ザクションを用いる方法の利点を示しなさい。
In 問題(1001) ,
write advantages of using transaction over using locking.
Argus 言語のガーディアンと次の言語の要素のどれかと比較しなさい。
重要な類似点1つと重要なガーディアンの優位性1つを述べなさい。
Compare guardians in Argus language with elements in either of the following languages.
Write an important similarity and an important advantage of guardians.
- Concurrent Pascal のモニタ Monitors in Concurrent Pascal
- Java の synchronized によるモニタ Monitors by "synchronized" in Java
- Pthread の mutex によるモニタ Monitors by mutex in C Pthread
Last updated: 2019/08/14 10:40:50
Yasushi Shinjo / <yas@cs.tsukuba.ac.jp>