FSP语言学习(十):用时态逻辑进行模型检查

目录

1. 引言

2. LTL

2.1 Action的逻辑

3. FSP—流动性

3.1 FSP– Indexed fluents

4. FSP – Fluent expressions

5. 时间逻辑—“always” 和 “eventually”

5.1 时间逻辑—“always”

5.2 时间逻辑—“eventually”

6. FSP – Safety and liveness

7. 结合时间性运算符

8. 时间逻辑法则

9. 时间逻辑的表达能力

10. 时间逻辑—“until”操作符

11. 时间逻辑—“next”操作符

12. 重新审视相互排斥

13. 表达Mutex的安全性和活泼性

14. 现实世界中的模型检查


1. 引言

在上两讲中,我们看到如何指定对并发系统的每一次执行都成立的属性。这些安全和有效性属性对于描述系统的某些属性非常有用。然而,它们并不像逻辑属性那样强大。

逻辑属性是由命题和逻辑运算符组成的描述,如and、not和or。你可能已经学过命题逻辑,它允许我们描述关于系统在某一瞬间的状态的属性。然而,FSP模型没有状态,它的时间是线性的,连续的,它们由在连续时间中发生的动作组成。

2. LTL

我们如何解决上述问题,为FSP模型添加命题逻辑。在这里,我们超越了命题逻辑和安全/有效性属性,而采用了线性时态逻辑(LTL)。

LTL谓词给了我们比命题逻辑或安全/有效性属性更大的灵活性,并允许我们指定更复杂的系统属性,然后可以用LTSA进行检查。

2.1 Action的逻辑

逻辑学中的原子命题涉及到系统的状态。在软件分析中,这通常是基本命题,如i \geq 0i < j。我们该如何思考并发模型的逻辑呢?首先我们提供一个思路:

因为在并发的模型中,我们通常关注的是action,以及它们发生的顺序。我们可以采用命题逻辑的思想,说当action a执行时,命题a是真的,而在执行其他action的时候命题a是假的。但这种思路有问题,因为每次只有一个命题是真的。

实际上,我们感兴趣的是关于一段时间内的行动序列的属性,而不仅仅是一个瞬间的时间。时间逻辑允许我们谈论持续时间和事件的相对时间(相对于绝对时间)。

3. FSP—流动性

fluent是一种可以随时间变化的属性。

fluent使我们能够描述一个系统在其生命周期中的属性,而不仅仅是在一个瞬间。

fluent被大量用于逻辑学,特别是人工智能。

在FSP中,一个fluent被描述为:

fluent FL = <{s1,...,sN}, {e1,...,eN}>

s1,...,sNe1,...,eN都是actions:

fluent FL

fluent FL是一个命题,当\left \{ s1,...,sN \right \}中的任何一个动作发生时,FL变成了True。而当\left \{ e1,...,eN \right \}中的任何一个动作发生时,FL又变为False。

如果我们想要一个最初是True的fluent,我们可以用以下方式来描述它:

fluent FL = <{s1,...,sN}, {e1,...,eN}> initially 1

1 代表 true,0 代表 false

以交通灯系统为例,当我们预计绿灯亮的时候,我们可以说:

fluent GREEN = <{green}, {yellow ,red}> initially 1

也就是说,当绿色动作发生时,fluent GREEN为真,而当黄色或红色发生时,fluent 为假。

因此,这个 fluent 不是一个动作:当黄色或红色以外的动作发生时(比如说,动作 wander 或者 none),它仍然是真的,尽管动作绿色目前不是真的。

3.1 FSP– Indexed fluents

与进程一样,我们可以为两个灯定义索引 fluents:

fluent GREEN[i:1..2]
    = <{green[i]}, {yellow[i],red[i]}> initially 1

4. FSP – Fluent expressions

Fluents 可以使用命题逻辑连接词进行组合。

此外,我们还有(有界的)普遍性和存在性量词:

forall[i:1..2] GREEN[i]
exists[i:1..2] GREEN[i]

现在我们可以表示,我们不希望两个灯同时是绿色的:

!forall[i:1..2] GREEN[i]

上面这种表达等价于:

!(GREEN[1] && GREEN[2])//不能12同时为绿色

也等价于:

exists[i:1..2] !GREEN[i] //12中至少存在一个不为绿色

5. 时间逻辑—“always” 和 “eventually”

到目前为止,我们指定了一个单一时间点的属性。它们的真假取决于到该点为止的轨迹。

为了表达与整个时间线有关的属性,我们有时间运算符。

时间运算符允许我们指定我们模型中所有轨迹的属性,因此也允许我们模型本身的属性。

两个最基本的运算符是“always”和“eventually”。

5.1 时间逻辑—“always”

如果公式F在当前时刻和未来每一时刻为真,则线性时间逻辑公式 [ ]F(读作 always F)为真。

always操作符是一个\square F,但 [ ] 是打字机字体的一个近似。

让我们为我们的交通灯定义一些fluents:

fluent GREEN = <{green}, {yellow ,red}> initially 1
fluent YELLOW = <{yellow}, {green ,red}> initially 0
fluent RED = <{red}, {yellow ,green}> initially 0

我现在想表达出,光总是绿色、黄色或红色的:

assert ALWAYS_A_COLOUR = [](GREEN || YELLOW || RED)

这显示了action和fluent之间的区别。属性 [ ](green || yellow || red) 在红绿灯系统中不成立,因为 button 和 none 可能发生。在这些时间点上,green、yellow或red都不成立,但我们的一个fluents会成立。所以我们不能写成action而应该写成process。

5.2 时间逻辑—“eventually”

线性时间逻辑公式<>F(读作 eventually F)为真,如果公式F在当前瞬间或在未来某个瞬间为真。

这个运算符是一个\lozenge F,但<>是打字机字体的一个近似值。

 我现在想表达出,光线最终会变成红色:

assert EVENTUALLY_RED = <>RED

注:使用LTSA检查这些属性,选择:Check → LTL Property → ALWAYS_A_COLOUR

6. FSP – Safety and liveness

always操作符被用来描述安全属性,而eventually操作符被用来描述liveness属性。

然而,这些操作符与使用属性关键字不同。时间运算符提供了更大的灵活性。例如,考虑这个终止系统 :

A = (a -> b -> END | c -> b -> END).

我们可以使用

assert B = <>b

来指定b是eventually会发生。这两种表达使用LTSA,都会报告没有违规。但第一种表达与我们的意思有明显偏差,我们想表达b不会无限地出现,它可能只发生了一次,只需要保证它最终会发生。使用LTL,我们可以表达b最终会发生,即使它只发生一次。

7. 结合时间性运算符

请注意,\lozenge F说的是F将成为真的,而不是说F从某一点开始一直是真的。为此,我们需要\lozenge \square F 

8. 时间逻辑法则

应该清楚的是,\square\lozenge是双重运算符,就像普遍量化和存在量化一样。这里是

9. 时间逻辑的表达能力

时间运算符可以任意地与通常的连接词结合。这使得我们可以表达并发系统的复杂属性。

假设进程P和进程Q同时运行,争夺一些资源。说p是P在其关键部分(拥有资源)时执行的动作,q是Q在其关键部分执行的动作。

那么p\rightarrow \lozenge q意味着,如果P目前处于它的关键部分,那么Q最终将进入它的关键部分。

因此\square \left ( p\rightarrow \lozenge q \right )表示Q不会被饿死。

10. 时间逻辑—“until”操作符

尽管如此,always和eventually运算符的表达能力还是有限的,因为它们是单数,每个运算符都适用于单个公式。通常情况下,正确性规范涉及几个命题的关系。我们可能想说P在Q进入它的临界段之前最多进入一次。until操作符允许我们指定某个属性为真,直到另一个属性变为真。

F U G是真的,如果G最终成为真的,而F在那一刻之前是真的。

在我们的交通灯系统中,最初是绿灯,并保持绿色,直到按下按钮为止:

assert INITIALLY_GREEN = (GREEN U button)

值得注意的是,一旦按钮被按下,灯可以保持绿色,同时仍然保留这个属性。断言中没有说绿色会变成假的。一旦按钮被按下,绿色(以及事实上的INITIALLY_GREEN)的truth就不再有意义了。

11. 时间逻辑—“next”操作符

“next”操作符允许我们指定某个属性在下一个瞬间为真。

The linear temporal logic formula X F is true iff F is true at the next instant.

“next instant” 我们指的是下一个动作发生的时间。

作为一个例子,我们可以指定当按钮被按下时,灯将在下一个瞬间变黄 

assert BUTTON_TO_YELLOW = (button -> X YELLOW)

在这里,我们可以很容易地用动作yellow来代替fluent YELLOW,其结果也是一样的。这是因为当yellow发生时,fluent YELLOW变为真。在green或red动作发生之前,它一直是真的,但在这种情况下,我们只关心按钮之后的下一个动作,所以YELLOW的持续时间并不重要。

12. 重新审视相互排斥

让我们再次考虑使用信号量对互斥现象进行建模。回想一下,我们有N个进程想要进入然后退出一个关键区域

LOOP
= (mutex.down -> enter -> exit -> mutex.up -> LOOP).
||N_LOOPS
= ( p[1..N]:LOOP
|| {p[1..N]}::mutex:SEMAPHORE(1)
).

在这里,up和down是信号量操作。

首先,我们定义一个fluent,当一个进程处于其关键部分时为真,并为所有进程定义这个fluent。当一个进程调用enter时,它处于临界区,而当它调用exit时则跳出临界区。

fluent IN_CRITICAL[i:1..N]
    = <{p[i].enter}, {p[i].exit}>

我们想指定两个重要的属性:一个是安全属性,即每次只有一个进程可以进入关键部分;另一个是liveness属性,即没有进程会starves。

13. 表达Mutex的安全性和活泼性

//only one thread in its critical section at one time
assert MUTEX_N
= []!(exists [i:1..N-1]
(IN_CRITICAL[i] && IN_CRITICAL[i+1..N]))
//all processes eventually enter the critical section
assert EVENTUALLY_ENTER
= forall[i:1..N] <>p[i].enter

第一个属性指出,总是不存在任何进程i,使该进程处于其关键部分,而另一进程也处于其关键部分。

这是相互排斥的两个最重要的属性。然而,我们可能还想验证某些其他属性。下面规定,在锁定互斥之前,没有进程进入其关键部分 :

assert NO_ENTER_BEFORE_MUTEX
    = forall[i:1..N]
        (!IN_CRITICAL[i] U p[i].mutex.down)

我们还可以规定,当一个进程锁定了mutex时,它在下一个tick时将处于其关键部分:

assert LOCKED_OUT
    = forall[i:1..N]
        (p[i].mutex.down -> X IN_CRITICAL[i])

这实际上是说,当一个进程锁定突变体时,其他进程不能做任何与突变体有关的事情:下一个动作是锁定的进程进入其关键部分。

最后,我们可能想要以下的响应属性,它规定,如果一个线程进入关键部分,它最终必须退出 :

assert MUST_EXIT
    = forall[i:1..N]
        [](p[i].enter -> <>p[i].exit)

这些属性在原始模型上都是成立的。

但是同样的,如果我们可以将信号灯从二进制信号灯改为允许值为0、1和2的信号灯,我们就会遇到违反MUTEX_N和LOCKED_OUT属性的情况。

LTSA的一个优点是它对LTL属性给出的报告。如果我们检查3值信号灯的MUTEX_N属性,LTSA报告说 

Trace to property violation in MUTEX_N:
    p.1.mutex.down
    p.1.enter         IN_CRITICAL.1
    p.2.mutex.down    IN_CRITICAL.1
    p.2.enter         IN_CRITICAL.1 && IN_CRITICAL.2

左边是trace,如前面所见。右边是相关的fluents,这些fluents都是真的。在p.1.enter之后,fluent IN_CRITICAL.1变成了真。在p.2.enter之后,fluent IN_CRITICAL.1和IN_CRITICAL.2都为真;也就是说,进程1和2都在关键部分。

为了省去我们单独检查每个属性的麻烦,我们可以创建一个命题,将所有需要的属性连在一起 

assert ALL_PROPERTIES
    = ( EVENTUALLY_ENTER
        && MUTEX_N
        && NO_ENTER_BEFORE_MUTEX
        && LOCKED_OUT
        && MUST_EXIT
      )

现在,我们只需要检查ALL_PROPERTIES。

如果发现有违规行为,那么我们就需要单独检查每个属性,找出违规的属性,但作为回归测试的一种形式,这种方法很有用。

14. 现实世界中的模型检查

模型检查规范(用时态逻辑变体编写)已经成为实践中的一个重要工具,用于证明并发系统、通信协议和硬件方面的正确性,如缓存一致性。  

1990年左右,Ken McMillan发现了一种使用二进制决策图和相关的强大算法来执行他所谓的符号模型检查的方法,这是一个重大的突破。突然间,对具有非常多的状态的系统进行自动推理成为可能,通常是1020个或更多。

麦克米伦用他的技术验证了安科瑞巨无霸多处理器的缓存协议的正确性。随机模拟对此并不有效,但麦克米伦能够追踪到一个潜在的处理器死锁并将其修复。从那时起,许多其他的电路和协议都用模型检查进行了验证。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值