SPIN とは 2010年度 年度 高信頼ソ 高信頼ソフトウェア トウ ア モデル検査器 Spin (1) 教室:54-403 教室:54 403 情報理工学科 上田 和紀 2 Simple(?) Promela Interpreter(?) の略 z 並行 ( ⊃ 分散) システムの検証ツール システムの検証ツ ル Promela というモデリング言語で記述した 並行シ 並行システムが対象 ムが対象 検証したい性質は LTL (linear temporal p logic, 線形時相論理) で記述する z ACM System Software Award 受賞 (2001) cf. UNIX, TeX, TCP/IP, WWW, Java, . . . z 多くのユーザ 多くのユ ザ z さまざまな高速化の工夫 (後述) 1 Promela とは Process Meta Language z プロトコル記述言語から発展 z 並行プロセス系の挙動のモデリング言語 ↔ プログラミング言語 z 並行処理に関連する機能 (通信, guarded commands) は Hoare の CSP (Communicating Sequential Processes, 1978) に類似 に類似. 基本制御構造は Dijkstra の Guarded Commands (1975) に基づくが少し異なる チャネルを用いた通信と同期 cf. f 共有変数, 共有変数 モニタ タ (Hoare, (H 1974) 3 Promela とは (参考) CSP との主な違い z プロセスの動的生成が可能 z チャネル通信では 同期通信 (ランデブ) と非同期通信 (バッ ファ通信) とがある プロセス名でなくてチャネル名を指定 チャネルを送受信対象にできる z 大域変数経由の通信も可能 低レベル並行処理機能の記述と検証のため 低 ベ 並行処理機能 記述と検証 ため z g goto あり 低レベル制御構造の記述と検証のため 4 Promela の特徴(cf. 他のプログラミング言語) 5 有限状態系 ― 網羅的な検査を可能にするため z プロセスは255個しか作れない 非同期実行 z 大域時計なし, プロセスの相対速度も不定 非決定的 (nondetermistic) な制御構造 z モデルの overspecification を避ける 個々の文 (statement) は “実行可能” 実行可能 (executable) であるための前提条件をもつ z 実行可能でなければ中断する 実行可能 なければ中断する Î 同期機構 例:q q ? m は, チャネル q にメッセージが届 いていなかったら届くまで待つ Guarded Commands (2): do ... od プロセスは手続きのように使え、引数も渡せる プロセスは手続きのように使え 引数も渡せる 関数のように起動側に値を返すにはどうするか? Max program cf cf. if ( (x > >= y) ) m = x else /* / asymmetric! */ / m = y Guarded Commands (2) do ... od 初期プロセスの定義 proctype gcd(chan C) { int x, y; C ? x, y; do :: x > y -> x = x - y :: x < y -> > y = y - x :: else -> break od; C ! x, y } 6 jSpin では Random ボタン でシミュレーション実行 proctype max(int ( x, y) ) { int m; if :: x >= y -> m = x Q: x == y のときは :: x <= y -> m = y どちらが選ばれるか ? fi; printf("%d¥n", m) } init { run max (72,99) } GCD program proctype gcd(int ( x, y) ) { do :: x > y -> > x = x - y :: x < y -> y = y - x :: else -> break od; printf("%d %d¥n", x, y) } init { run gcd (72,16) } 7 Guarded Commands (1): if ... fi init { chan C = [0] of { int, int }; int x, y; run gcd(C); C ! 72, 20; C ? x, y; printf("%d %d¥n",x,y) } プロセス起動時に通信用チャネルを渡せば, あとの 受け渡し (通信) はチャネル経由で行える z cf. 入出力 8 並行処理 (concurrency) の基本 9 #define N 5 int x = 0; 複数のプロセスが並行動作しても, 共有資源 (大域 変数やチャネル) をめぐる競合や協調がなければ難 しいことも面白いことも起きない z cf. cf 人間社会 共有変数へのアクセス競合は相互排他 (mutual exclusion) l ) アルゴリズムで解決 ゴ ズム 解決 共有チャネルを用いた通信は必然的に (書き手と読 み手の間の) 同期を伴う z ただし, 一本のチャネルの書き手または読み手 本のチャネルの書き手または読み手 が複数あるときは, それら同士は競合関係 競合を考える場合は, 競合を考える場合は 言語の不可分動作 (atomic operations) を明確にしておく必要がある #define N 5 int x; active proctype inc() { do :: x < N -> x++ od } active proctype dec() { do :: x > 0 -> x-od } active proctype reset() { do :: x == N -> x = 0 od } active proctype inc() { do :: x < N -> x++ od } $ spin -a incdec.pml jSpin では y ボタン $g gcc -o p pan p pan.c Verify をクリック $ ./pan pan: assertion violated (( ((x>=0)&&(x<=5)) 0)&&( 5)) (at ( depth d h 22) pan: wrote incdec.pml.trail active proctype check() { assert (x >= 0 && x <= N) } Safety property(悪い ことは起きない)の表明 Q: 左のプログラム(止ま ) x は常に 0 以 らない)で 上 N 以下の範囲の中を動 くか? active proctype reset() { do :: x == N -> x = 0 od d } 監視プロセスを定義して, 検証モードで一緒に実行する シミュレーションモードと検証 モードの本質的な違いに注意! 10 active ti proctype t dec() d () { do :: x > 0 -> x-od } 11 並行プロセスとその動作検証 並行プロセスとその動作検証 並行プロセスとその動作検証 $ spin -p -t incdec.pml jSpin では Guided Starting inc with pid 0 ボタンをクリック St ti dec Starting d with ith pid id 1 Starting reset with pid 2 Starting check with pid 3 1: proc 0 (inc) line 6 "incdec.pml" (state 1) [((x<5))] 2: proc 0 (inc) line 6 "incdec.pml" (state 2) [x = (x+1)] ... 20: proc 1 (dec) line 12 "incdec.pml" (state 1) [((x>0))] 21: proc 2 (reset) line 18 "incdec.pml" (state 2) [x = 0] 22: proc 1 (dec) line 12 "incdec.pml" incdec.pml (state 2) [x = (x (x-1)] 1)] spin: line 23 "incdec.pml", Error: assertion violated spin: text of failed assertion: assert(((x>=0)&&(x<=5))) 23: proc 3 (check) line 23 "incdec incdec.pml pml" (state 1) [assert(((x>=0)&&(x<=5)))] spin: trail ends after 23 steps #processes: 4 x = -1 12 Promela の不可分操作 13 個々の文は必ずアトミックに実行される z 二つのプロセスが x = x + 1 (x++) を実行する と x の値は必ず 2 増加する (自明ではない) z チャネルへの出力データはなくならない チャネルへの出力デ タはなくならない x > 0 -> x–– (x > 0; x–– と同義) はアトミック に実行されない atomic{ x > 0 -> x–– } は, x > 0 が成り立つ 限りアトミ クに実行される 限りアトミックに実行される z x > 0 が成り立たないと当該プロセスの実行が 中 中断して他プロセスが実行されることがある 他プ が実 され があ 中断の可能性がない「基本ブロック」は atomic のかわりに d_step で囲むと効率的 相互排他のための抽象データ型 フィールド: フィ ルド z binary semaphore の場合 : bit s = 1; z counting semaphore の場合 : int s = N; 操作 (メソッド) : z P (wait, request) : atomic { s > 0; s-- } z V (signal, (signal release) : s++ ++ 参考:相互排他・同期機構の歴史 “Software” アルゴリズム z Dekker (1962), (1962) Peterson (1981) セマフォ z 相互排他用変数の操作に不可分性を付与 モ モニタ タ (Java の synchronized オブジ オブジェクト) クト) z 操作対象データと相互排他とを統合 チャネル通信 (CSP, (CSP CCS CCS, π 計算, 計算 . . .)) z データ自身を同期機構とする 制約概念に基づく通信 (並行制約プログラミング) グラフ書換え系 (LMNtal, B Bigraphs, graphs, . . .) Software transactional memory (STM) 14 セマフォ (semaphore) 15 実際の並行プログラミングに使われる最低レベル の機能 z 現実のハードウェア命令 (test-and-set, compare-and-swap, etc.) のレベルと符合 16 セマフォ (semaphore) Promela での抽象化法 z #define #d fi または inline i li を使った定義 #define P(s) #define V(s) atomic { s > 0 -> s-- } s++ #define や inline は言語機構としては ”二級” (second class) 言語の意味論に組み込まれていない z チャネルを使った定義 プロセス ↔ オブジェクト チャネル通信 ↔ メソッド呼出し+戻り チャネル通信 17 chan name = [n] of { T1,...,Tn } 使った非同期通信 z n = 0 : ランデブ デブ (rendezvous) ( d ) 通信 1 本のチャネルに複数のプロセスが書くことも読む こともできる ランデブ通信を除けば, 1 ステップで実行される プロセスは系全体で 1 個 (interleaving) ランデブ通信では 2 個のプロセスが同時に実行さ れる (handshake) z 通信手段であると同時に強力な同期手段 mtype = { p, v }; chan S = [ [0] ] of { mtype yp } }; active proctype sem() { bit count = 1; do :: count > 0 -> すぐに 5 個起動 S?p; count-active[5] proctype c() { :: count == 0 -> do S?v; :: S!p; count++; 排他的な仕事; od } S!v od } 18 mtype = { p, v }; chan S = [0] of { mtype }; active proctype sem() { do すぐに 5 個起動 :: S?p; S?v; od active[5] proctype c() { } do :: S!p; Q: “?” と “!” 排他的な仕事; を入れ替えて も大丈夫か? S!v od } z n > 0 : FIFO チャネル (= ( queue, u u buff buffer)) を Binary Semaphore (Version 2) Binary Semaphore (Version 1) 19 Promela モデル(cf. プログラム)の構成 宣言: 名前に逆順に番号を振る z ユーザ定義型宣言 ユ ザ定義型宣言 mtype = { Nn,...,N1 } (cf. 列挙型) typedef (レコード型専用, cf. C 言語) z チャネル宣言 chan name = [n] of { T1,...,Tn } z 大域変数宣言 z プロセス宣言 初期プロセス: z init { p process } 各メッセージの構造 bounded buffer の容量 0 ならばランデブ通信 20 Promela モデル(cf. プログラム)の型 21 基本データ型(ビット数) z bit(1), bit(1) bool(1) bool(1), byte(8) byte(8), chan(8) chan(8), mtype(8) mtype(8), pid(8), short(16), int(32), unsigned(n) unsigned dw:3=5 5; bit, bool の占有容量は 8 ビット 数値変数 (local + global) の暗黙の初期値はゼロ 配列 typedef Pair { z byte a[12]; short f1; z chan h c[10]; [10] byte f2; 構造体 } 型検査は動的 skip : いつも実行可能で何もしない.1 と同じ z true t u も同義 false : 0 と同じ.文としては “実行不能” の意味に なる else : 当該プロセスの中の他の文がどれも実行でき ないときに真 (実行可能) timeout : モデル全体で他に実行できるものがない デル全体で他に実行できるものがない ときに真 (実行可能) z else の大域版 _nr_pr : 実行中のプロセスの数 _pid id : 当該プロセスのプロセス番号 当該プ セ のプ セ 番号 22 基本文 z 代入 x++ x-x x x+1 x=run x=x+1 x un P() z 式文 x>0 run P() skip true else timeout z 送信 c ! m チャネルが満杯のときは中断 z 受信 c ? m 変数で受けても定数で受けても良い c ? print(x) は c ? print, x と同義 z assert(x >= 0) ( ) 内が 0 だとエラー発生 z 出力 pr printf(“x ntf( x = %d¥n %d¥n”,, x) 他に複合文や制御文がある skip, true, false, else, timeout, . . .(式文) Promela モデル(cf. プログラム)の文 23 複合文 if . . . fi と do . . . od (ネスト可) z 各選択肢の最初の文をガードという 各選択肢の最初の文をガ ドという その選択肢が実行を始められるかを決める z ガードのうちの 1 つ以上が実行可能ならば全体 が実行可能 atomic { } と d_step { } z 最初の文が実行可能ならば全体が実行可能 z goto による出入りがなく, かつ中断の可能性が なければ d_step なければ, d step を使った方が速い 複合文はどれも厳密には文ではなく, 「状態遷移の 形を定める機能 と位置づけられ いる 形を定める機能」と位置づけられている 24 if ... fi, do ... od 25 Guarded Commands や CSP の場合 z ガードには ガ ドには 0 個以上の条件や変数宣言+受信 (中断はするが失敗はしない) を書ける z if f のガードがどれも失敗ならば ガ ドがどれも失敗ならば abort b (f (fail) l) z do のガードがどれも失敗ならば終了 Promela の場合 z 条件判定も受信も, 条件判定も受信も 成立するまで実行中断 z if や do を脱出するためには, 脱出するための 選択肢 (典型的には else) を明示的に用意する break : do . . . od から抜け出すのに使う goto t : 指定されたラベルの場所へ飛ぶ z break は常に goto を使って書き直せる z プロトコルや状態遷移をモデル化するのに便利 制御文も厳密には文ではなく, ひと ひとつ前の文の「次 前の文の 次 の制御状態」を定めるもの プロセス宣言 active[n] proctype <名前> ( <仮引数宣言リスト> ) { <本体> 本体> } z <仮引数宣言リスト> はセミコロンで区切る z <本体> 本体 には局所変数宣言や文を並 には局所変数宣言や文を並べる る プロセスは run で作る.run はプロセス id を返す (が受け取らなくてもよい) プロセスはプロセス内 (が受け取らなくてもよい).プロセスはプロセス内 で動的に作ることができる プロセスの実行は, プロセスの実行は run の終了後に始まる active[n] がついていると n 個は初期プロセスとし て起動される 起動される (が別途 run で追加生成可能) 追加生成可能) 26 制御文 27 プロセスの動的生成:Small Set of Integers mtype = { has, insert }; chan reply = [0] of { int }; proctype element(chan c) { int m, n; } init { bool ans; chan d = [0] of { mtype, int }; run element(d); d!insert(6); d!insert(3); d!has(3); reply?ans; d!has(4); reply?ans; } cf. C.A.R. Hoare, C Communicating i ti S Sequential ti l Processes, CACM, Vol.21, No.8 (1978), p.672. 28 do :: c ? has(m) -> reply ! false :: c ? insert(n) -> > chan d = [0] of { mtype, int }; run element(d); do :: c ? has(m) -> if :: m <= < n -> > reply ! (m==n) (m n) :: else -> d! has(m) fi :: c ? insert(m) i t( ) -> if :: m < n -> d ! insert(n); n=m :: m == n -> skip :: m > n -> d ! insert(m) fi od od Small Set of Integers: 説明 29 chan reply = [0] of { int }; プロセスとチャネルで作られる線形リストで集合 を表現 各プロセスが 1 個の整数を保持 z 最後のプロセスだけは, 最後 プ だけは 整数を保持しない番兵 整数を保持 な 番兵 (sentinel) 集合の要素は昇順に整列された状態を維持 番兵プ 番兵プロセスは外側の セスは外側の do ル ループを回り, プを回り, 他のプ 他のプロ セスは内側の do ループを回る パイプライン並列性をもつ has メッセージへの返事は, 全員が共有する reply チャネルを通じて行う Sieve of Eratosthenes: 説明 素数の倍数をふるい落とすフィルタプロセスを各 素数ごとに作り 線形リスト状に接続する 素数ごとに作り, 各フィルタプロセスは, z 担当する素数を reply l チャネル ネ (共有) を通じて を通じ まず報告する. z その後, 自分の一つ前のフィルタプロセスから 素数候補が順次送られてくる 最初に送られてくる数は素数なので (Bertrand仮説), 新たなチャネルを作って自 分の後続フィルタプロセスを生成する その後, その後 自分が担当する素数の倍数以外を 自分の後続プロセスに転送してゆく プロセスの動的生成:Sieve of Eratosthenes proctype sieve(chan c; int p) { i t mp, m; int reply ! p; mp = p; c ? m; chan h d = [0] off { int i t }; } run sieve(d, m); do :: c ? m -> > do :: m > mp -> mp = mp + p :: else -> > break b eak od; if :: m == mp -> > skip :: m < mp -> d ! m fi od } 31 #define N 50 active proctype sieve2() { p y ! 2;; reply chan c = [0] of { int }; run sieve(c, 3); int n = 5;; do :: n < N -> c ! n; n = n + 2 :: else -> break od } init { int n; do :: reply ? n -> > printf("%d¥n", printf("%d¥n" n) od } cf. C.A.R. Hoare, Communicating Sequential Processes, CACM, Vol.21, No.8 (1978), p.674. モデルの満たすべき性質 ― 伝統的分類 安全性 (safety) ―「悪いことが決して起きない」 z 悪いことの例: 悪いことの例 assertion 違反 止まってはいけない場所で待ち状態に入る 活性 (liveness) ―「良いことがいずれ起きる」 良い とがいずれ起きる」 z 良いことの例: リクエストを出すと必ず返事がある 定期的な通信が途絶えない 検証系の立場からは「良い」「悪い」よりも, どの ような状態や状態列が { 必ず起きる|起きるかも しれない | 絶対起きない } かで考える方がよい 30 32 モデルの満たすべき性質 ― 技術的分類 33 状態に関する性質 (state property) の例: z a + b の値は常に 0 以上 (不変量, (不変量 invariant) in i nt) z 35行目では a > b (assertion) z デッドロックしない (= 実行終了時にはすべて のプロセスが本体の実行を終えている) 終了状態 (end state) に関する性質 状態列に関する性質 (path property) の例: z 性質 P を満たす状態が発生したら, いずれ性質 Q を満たす状態が起きる (response) z 性質 P を満たす状態は何回でも到来する Non-progress cycle ― progress ラベルの場所を無 限回通ってくれない無限ル プ 限回通ってくれない無限ループ Non-progress cycle を検出するには, z jSpin では, は Verify V f ボタンの左のメニューから ボタ 左 メ から Non-progress を選んで Verify z 通常の Spin では p pan.c のコンパイル時に –DNP をつける pan.c の実行時に –l をつける progress ラ ラベルが複数個存在するときは, ルが複数個存在するときは, そのう ちのどれかを無限回通っていれば計算が進んでい ると見なされる z 例題: sem1b.pml, sem1c.pml 34 状態 = 各変数値 × チャネル内容 × 制御状態 検証時に特別な意味を持つラベル: 検証時に特別な意味を持つラベル z progress : 「ここを無限回通っていれば世の中 全体 計算が進ん 全体の計算が進んでいると言える」 ると言える z accept p : 「ここを無限回通ったら世の中全体の 計算が進んだことにならない」 z end : 「世の中全体の計算が止まったときここ 世の中全体の計算が止まったとき にいてもよい」 ラベル名は尾ひれがついてもよい z end1, end2, end_service, . . . プロセス内に複数の プ セ 内に複数の end d state が書ける 制御の状態に特別なラベルをつける 制御の状態に特別なラベルをつける 35 制御の状態に特別なラベルをつける Acceptance cycle ― accept ラベルの場所を無限 回通ってしまう無限ル プ 回通ってしまう無限ループ z 後述の LTL モデル検査では “望ましくない無限 実行” (エラー) (エラ ) を acceptance c cycle cle で表現 Acceptance cycle を検出するには z jSpin では, Verify ボタンの左のメニューから Acceptance p を選んで Verify y z 通常の Spin では pan.c の のコンパイル時に ン イル時に –DNP DNP を をつけない けない pan.c の実行時に –a をつける アルゴリズムの停止性の検証にも使える z 例題: gcd-accept.pml 36 制御の状態に特別なラベルをつける Invalid endstate ― すべてのプロセスが本体の一 番最後に到達する前に計算が停止した (進まなく なった) 場合, エラーが報告される z 最後以外の場所で止まって 最後以外の場所で止ま て (待ち状態に入って) (待ち状態に入 て) も問題ない場合, そこに end ラベルを立てる 例: 例 サーバプロセスの受信待ちの場所など サ バプ セ 受信待ち 場所など Invalid endstate は通常の検証モード実行で検出 される z jSpin では, Verify ボタンの左のメ ボタンの左のメニューから から Safety を選んで Verify 37
© Copyright 2024 Paperzz