彼得森算法的这种模型不正确吗?

时间:2018-10-08 02:10:48

标签: model-checking promela spin

我写了以下彼得森算法的模型:

bool want[2], turn

ltl { []<>P[0]@cs }

active [2] proctype P() {
    pid me = _pid
    pid you = 1 - me

    do
    :: want[me] = true
       turn = you
       !want[you] || turn == me
cs:    want[me] = false
    od
}
我的理解是,如线性时间逻辑要求中所述,该算法提供了免于饥饿的自由。那为什么在分析模型时会出现错误?

ltl ltl_0: [] (<> ((P[0]@cs)))
pan:1: acceptance cycle (at depth 2)
pan: wrote peterson.pml.trail

(Spin Version 6.4.8 -- 2 March 2018)
Warning: Search not completed
    + Partial Order Reduction

Full statespace search for:
    never claim             + (ltl_0)
    assertion violations    + (if within scope of claim)
    acceptance   cycles     + (fairness disabled)
    invalid end states  - (disabled by never claim)

State-vector 36 byte, depth reached 9, errors: 1
        5 states, stored
        0 states, matched
        5 transitions (= stored+matched)
        0 atomic steps
hash conflicts:         0 (resolved)

Stats on memory usage (in Megabytes):
    0.000   equivalent memory usage for states (stored*(State-vector + overhead))
    0.291   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

1 个答案:

答案 0 :(得分:2)

是的, Peterson算法应该没有饥饿,而且确实如此。

饥饿发生在进程请求一些资源时,但它被永久拒绝了它们。因此, progress 公式的更好编码将是:

ltl p1 { [] (P[0]@req -> <> (P[0]@cs) }

其中req是如下放置的标签:

    do
    :: true ->
req:   want[me] = true
       turn = you
       !want[you] || turn == me
cs:    want[me] = false
    od

不幸的是,以前的公式仍然是false

这样做的原因是,您正在进行模型检查的系统的流程调度程序通常不是公平。实际上,它允许执行,其中永远不会选择_pid等于0的进程执行。

spin模型检查器为您提供了一个恰恰在这种情况下的反例:

~$ spin -t -g -l -p t.pml
ltl ltl_0: [] (<> ((P[0]@cs)))
starting claim 1
using statement merging
Never claim moves to line 3 [(!((P[0]._p==cs)))]
  2:    proc  1 (P:1) t.pml:10 (state 1)    [want[me] = 1]
        want[0] = 0
        want[1] = 1
  <<<<<START OF CYCLE>>>>>
Never claim moves to line 8 [(!((P[0]._p==cs)))]
  4:    proc  1 (P:1) t.pml:11 (state 2)    [turn = you]
  6:    proc  1 (P:1) t.pml:12 (state 3)    [((!(want[you])||(turn==me)))]
  8:    proc  1 (P:1) t.pml:13 (state 4)    [want[me] = 0]
        want[0] = 0
        want[1] = 0
 10:    proc  1 (P:1) t.pml:10 (state 1)    [want[me] = 1]
        want[0] = 0
        want[1] = 1
spin: trail ends after 10 steps
#processes: 2
        want[0] = 0
        want[1] = 1
        turn = 0
        cs = 0
 10:    proc  1 (P:1) t.pml:11 (state 2)
 10:    proc  0 (P:1) t.pml:9 (state 5)
 10:    proc  - (ltl_0:1) _spin_nvr.tmp:7 (state 10)
2 processes created

解决方法。

对于此问题,基本上有两种解决方法:

  • 首先是要问,如果某个过程试图进入关键部分,那么某个过程最终会进入它:

    ltl p2 { [] ((P[0]@req || P[1]@req) -> <> (P[0]@cs || P[1]@cs) }
    

    这可以确保整个系统都有进步,但是不能保证P[0]P[1]中的任何一个都不会导致饥饿

  • 第二个是引入公平条件,该条件要求将模型检查的重点仅放在计划无数次执行流程的执行上:

    ltl p3 { ([]<> (_last == 0)) -> [] (P[0]@req -> <> (P[0]@cs)) }
    

    其中_last是预定义的内部变量,在docs中进行了如下描述:

      

    说明   _last是pid类型的预定义的全局只读变量,该变量保存执行最后一个操作的进程的实例化编号。   按当前执行顺序执行。 _last的初始值为   零。

         

    _last变量只能在从不声明中使用。它是一个   在任何情况下为该变量赋值的错误。

    不幸的是,当检查模型中的缺乏饥饿时,这种方法不是很好。这是因为要求[] <> _last == 0不仅会删除由于调度程序的不公平而没有经常对P[0]进行无限调度的执行,而且还会删除其中P[0]未被执行的情况由于饥饿的实际问题而安排了时间。


适当的解决方案。

我建议更改您的模型,以便P[0]在等待自己的回合时执行繁忙等待,而不是阻塞。这样可以在尝试证明没有饥饿的情况下使用_last带来的麻烦。最终模型将是:

bool flag[2], turn

active [2] proctype P() {
    pid i = _pid;
    pid j = 1 - i;

    do
    :: true ->
req:    flag[i] = true
        turn = j;
        do
            :: (flag[j] && (turn == j)) -> skip
            :: else -> break;
        od;
cs:     skip;
        flag[i] = false;
    od
}

ltl p1 { (
            ([]<> (_last == 0)) &&
            ([]<> (_last == 1))
         ) ->
            ([] (P[0]@req -> <> (P[0]@cs)))
       }

并且确实在不丢弃任何潜在有趣的执行跟踪的情况下验证了该属性:

~$ spin -a t.pml
ltl p1: (! (([] (<> ((_last==0)))) && ([] (<> ((_last==1)))))) || ([] ((! ((P[0]@req))) || (<> ((P[0]@cs)))))
~$ gcc pan.c
~$ ./a.out -a 

(Spin Version 6.4.8 -- 2 March 2018)

Full statespace search for:
    never claim             + (p1)
    assertion violations    + (if within scope of claim)
    acceptance   cycles     + (fairness disabled)
    invalid end states  - (disabled by never claim)

State-vector 40 byte, depth reached 97, errors: 0
      269 states, stored (415 visited)
      670 states, matched
     1085 transitions (= visited+matched)
        0 atomic steps
hash conflicts:         0 (resolved)

Stats on memory usage (in Megabytes):
    0.017   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


unreached in proctype P
    t.pml:18, state 16, "-end-"
    (1 of 16 states)
unreached in claim p1
    _spin_nvr.tmp:23, state 33, "-end-"
    (1 of 33 states)

pan: elapsed time 0 seconds

请注意,我们要求同时允许P[0]P[1]无限执行,因为否则会发现另一个虚假的反例。


  

这个彼得森算法的模型不正确吗?

因此,要更直接地回答您的问题,您的模型在功能上不是错误的,但是要适当地验证是否没有饥饿,有必要进行一些小的更改。