在以下代码示例中,发件人和接收器交换 - 未确定的时间 - 多个数据包。由发件人发送的每封邮件都包含0
或1
以及序列号。每当 Receiver 消息时,它会检查它是否为新消息,在这种情况下,它会向发件人发送确认。收到ACK
后,发件人会发送包含新内容的邮件。
在此模型中,无论何时发送消息,消息都可能丢失,发送两次或正常发送。
mtype = { MESSAGE, ACK };
chan sender2receiver = [2] of { mtype, bit, int };
chan receiver2sender = [2] of { mtype, bit, int };
inline unreliable_send(channel, type, tag, seqno) {
bool loss = false;
bool duplicate = true;
if
:: channel!type(tag, seqno);
if
:: channel!type(tag, seqno);
:: duplicate = false;
fi
:: loss = true;
fi
}
active proctype Sender () {
bit in_bit, out_bit;
int seq_no;
do
:: unreliable_send(sender2receiver, MESSAGE, out_bit, seq_no) ->
receiver2sender?ACK(in_bit, 0);
if
:: in_bit == out_bit ->
out_bit = 1 - out_bit;
seq_no++;
:: else ->
skip;
fi;
od;
}
active proctype Receiver () {
bit in_bit, old_bit;
int seq_no;
do
:: sender2receiver?MESSAGE(in_bit, seq_no) ->
if
:: in_bit != old_bit ->
printf("received: %d\n", seq_no);
old_bit = in_bit;
:: else ->
skip;
fi;
unreliable_send(receiver2sender, ACK, in_bit, 0);
od;
}
在之前的模型中,丢失数据包会导致死锁。
为了解决这个问题,讲师讨论了几种方法。
其中一种方法是让发件人无情,以便它一直发送消息,直到它有效地传递给接收器及其相应的发件人正确接收了ack
。的即:
active proctype Sender () {
bit in_bit, out_bit;
int seq_no;
// solution 1: keep sending a message till an ack is received
do
:: unreliable_send(sender2receiver, MESSAGE, out_bit, seq_no);
:: receiver2sender?ACK(in_bit, 0);
if
:: in_bit == out_bit ->
out_bit = 1 - out_bit;
seq_no++;
:: else ->
skip;
fi;
od;
}
虽然在实践中这种方法似乎有效,这意味着模拟模型不再导致僵局,讲师警告我们,它仍然会通过正式验证的步骤,因为至少退出一个不幸的执行路径,在某些时候所有消息都会丢失,无论多少人坚持再次发送相同的消息。
讲师邀请我们思考如何使用Spin
找到提议方法失败的执行跟踪之一。由于我们尚未涵盖LTL
模型检查,因此解决方案应基于assert
或标签。
答案 0 :(得分:0)
该协议背后的理念是发件人和接收器不断交换新邮件。
可以通过进度标签来验证提议的解决方案是否有效地遵守了此规范。
进度标签用于定义正确性声明。进步 label表示标记的全局状态必须是的要求 经常在任何无限的系统执行中无限访问。任何 验证者可以将违反此要求的行为报告为 非进展周期。
在这个模型中,进度状态是我们确定消息被正确发送和确认的状态,因此我们的进度状态就是我们意识到这一事实之后的状态。
因此,我们按如下方式修改发件人代码,并在progress:
之后添加in_bit == out_bit ->
标签:
active proctype Sender () {
bit in_bit, out_bit;
int seq_no;
// solution 1: keep sending a message till an ack is received
do
:: unreliable_send(sender2receiver, MESSAGE, out_bit, seq_no);
:: receiver2sender?ACK(in_bit, 0);
if
:: in_bit == out_bit ->
progress:
out_bit = 1 - out_bit;
seq_no++;
:: else ->
skip;
fi;
od;
}
然后我们可以正式验证模型如下:
~$ spin -a -DNP test.pml
~$ gcc -DNP pan.c -o run
~$ ./run -l -bfs
或者,使用以下单行:
~$ spin -search -l test.pml
正如所料,spin
找到非进度周期:
pan:1: non-progress cycle (at depth 16)
pan: wrote test.pml.trail
(Spin Version 6.4.7 -- 19 August 2017)
Warning: Search not completed
+ Partial Order Reduction
Full statespace search for:
never claim + (:np_:)
assertion violations + (if within scope of claim)
non-progress cycles + (fairness disabled)
invalid end states - (disabled by never claim)
State-vector 88 byte, depth reached 41, errors: 1
21 states, stored
0 states, matched
21 transitions (= stored+matched)
0 atomic steps
hash conflicts: 0 (resolved)
Stats on memory usage (in Megabytes):
0.002 equivalent memory usage for states (stored*(State-vector + overhead))
0.287 actual memory usage for states
128.000 memory used for hash table (-w24)
0.534 memory used for DFS stack (-m10000)
128.730 total actual memory usage
pan: elapsed time 0 seconds
我们现在可以模拟 反例并显示打印违规执行曲目:
~$ spin -p -l -g -t test.pml
starting claim 2
spin: couldn't find claim 2 (ignored)
using statement merging
2: proc 0 (Sender:1) test.pml:8 (state 1) [loss = 0]
Sender(0):loss = 0
2: proc 0 (Sender:1) test.pml:9 (state 2) [duplicate = 1]
Sender(0):duplicate = 1
Sender(0):loss = 0
4: proc 0 (Sender:1) test.pml:10 (state 3) [sender2receiver!2,out_bit,seq_no]
queue 1 (sender2receiver): [MESSAGE,0,0]
6: proc 1 (Receiver:1) test.pml:44 (state 1) [sender2receiver?MESSAGE,in_bit,seq_no]
queue 1 (sender2receiver):
Receiver(1):seq_no = 0
Receiver(1):in_bit = 0
8: proc 1 (Receiver:1) test.pml:49 (state 5) [else]
queue 1 (sender2receiver):
10: proc 1 (Receiver:1) test.pml:50 (state 6) [(1)]
queue 1 (sender2receiver):
12: proc 1 (Receiver:1) test.pml:8 (state 9) [loss = 0]
queue 1 (sender2receiver):
Receiver(1):loss = 0
12: proc 1 (Receiver:1) test.pml:9 (state 10) [duplicate = 1]
queue 1 (sender2receiver):
Receiver(1):duplicate = 1
Receiver(1):loss = 0
14: proc 1 (Receiver:1) test.pml:10 (state 11) [receiver2sender!1,in_bit,0]
queue 1 (sender2receiver):
queue 2 (receiver2sender): [ACK,0,0]
16: proc 1 (Receiver:1) test.pml:12 (state 12) [receiver2sender!1,in_bit,0]
queue 1 (sender2receiver):
queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
<<<<<START OF CYCLE>>>>>
18: proc 0 (Sender:1) test.pml:12 (state 4) [sender2receiver!2,out_bit,seq_no]
queue 1 (sender2receiver): [MESSAGE,0,0]
queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
20: proc 1 (Receiver:1) test.pml:44 (state 1) [sender2receiver?MESSAGE,in_bit,seq_no]
queue 1 (sender2receiver):
queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
Receiver(1):seq_no = 0
Receiver(1):in_bit = 0
22: proc 1 (Receiver:1) test.pml:49 (state 5) [else]
queue 1 (sender2receiver):
queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
24: proc 1 (Receiver:1) test.pml:50 (state 6) [(1)]
queue 1 (sender2receiver):
queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
26: proc 1 (Receiver:1) test.pml:8 (state 9) [loss = 0]
queue 1 (sender2receiver):
queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
Receiver(1):loss = 0
26: proc 1 (Receiver:1) test.pml:9 (state 10) [duplicate = 1]
queue 1 (sender2receiver):
queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
Receiver(1):duplicate = 1
Receiver(1):loss = 0
28: proc 1 (Receiver:1) test.pml:15 (state 16) [loss = 1]
queue 1 (sender2receiver):
queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
Receiver(1):loss = 1
30: proc 0 (Sender:1) test.pml:8 (state 1) [loss = 0]
queue 1 (sender2receiver):
queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
Sender(0):loss = 0
30: proc 0 (Sender:1) test.pml:9 (state 2) [duplicate = 1]
queue 1 (sender2receiver):
queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
Sender(0):duplicate = 1
Sender(0):loss = 0
32: proc 0 (Sender:1) test.pml:10 (state 3) [sender2receiver!2,out_bit,seq_no]
queue 1 (sender2receiver): [MESSAGE,0,0]
queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
34: proc 1 (Receiver:1) test.pml:44 (state 1) [sender2receiver?MESSAGE,in_bit,seq_no]
queue 1 (sender2receiver):
queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
Receiver(1):seq_no = 0
Receiver(1):in_bit = 0
36: proc 1 (Receiver:1) test.pml:49 (state 5) [else]
queue 1 (sender2receiver):
queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
38: proc 1 (Receiver:1) test.pml:50 (state 6) [(1)]
queue 1 (sender2receiver):
queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
40: proc 1 (Receiver:1) test.pml:8 (state 9) [loss = 0]
queue 1 (sender2receiver):
queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
Receiver(1):loss = 0
40: proc 1 (Receiver:1) test.pml:9 (state 10) [duplicate = 1]
queue 1 (sender2receiver):
queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
Receiver(1):duplicate = 1
Receiver(1):loss = 0
42: proc 1 (Receiver:1) test.pml:15 (state 16) [loss = 1]
queue 1 (sender2receiver):
queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
Receiver(1):loss = 1
spin: trail ends after 42 steps
#processes: 2
queue 1 (sender2receiver):
queue 2 (receiver2sender): [ACK,0,0][ACK,0,0]
42: proc 1 (Receiver:1) test.pml:43 (state 20)
Receiver(1):loss = 1
42: proc 0 (Sender:1) test.pml:11 (state 6)
2 processes created