Promela 中 N 个进程之间的锁定

Lock between N Processes in Promela

我正在尝试在 promela 中为我的一个项目建模以进行模型检查。在那里,我在网络中有 N 个节点。因此,对于每个节点,我都在制作一个过程。像这样:

init {
byte proc;
atomic {
    proc = 0;
    do
    :: proc < N ->
        run node (q[proc],proc);
        proc++
    :: proc >= N ->
        break
    od
}
}

所以,基本上,这里每个 'node' 都是模拟我网络中每个节点的过程。现在,Node Process 有 3 个线程,在我原来的实现中 运行 是并行的,在这三个线程中我锁定了某些部分,这样三个线程就不会同时访问临界区。所以,在 promela 中,我做了这样的事情:

proctype node (chan inp;byte ppid)
{
   run recv_A()
   run send_B()
   run do_C()
}

所以这里 recv_A、send_B 和 do_C 是网络中每个节点上并行 运行 的三个线程。现在,问题是,如果我使用 atomic 在 recv_A、send_B、do_C 中加锁,那么它会在所有 3*N 个进程上加锁,而我想要一个这样的锁锁定应用于三个一组。也就是说,如果 process1 的(从 recv_A 到 运行 的主节点进程)recv_A 在其 CS 中,则只有 process1 的 send_B 和 do_C 应该是禁止进入CS而不是进程2的recv_A、send_B、do_C。有办法吗?

您有多种选择,所有选择都围绕着在 N 个进程之间实施某种互斥算法:

Black & White Bakery 算法 的实现可用 here。但是请注意,这些算法 - 可能除了 Peterson 的一个 - 往往很复杂并且可能使您的系统验证不切实际。

一种比较简单的方法是求助于测试和设置算法,然而,它仍然使用atomic 尝试部分。这是取自 here.

的示例实现
bool lock = false;
int counter = 0;

active [3] proctype mutex()
{
  bool tmp = false;

trying:
  do
    :: atomic {
       tmp = lock;
       lock = true;
       } ->
       if
          :: tmp;
          :: else -> break;
       fi;
  od;

critical:
    printf("Process %d entered critical section.\n", _pid);
    counter++;
    assert(counter == 1);
    counter--;

exit:
    lock = false;
    printf("Process %d exited critical section.\n", _pid);

goto trying;
}

#define c0 (mutex[0]@critical)
#define c1 (mutex[1]@critical)
#define c2 (mutex[2]@critical)
#define t0 (mutex[0]@trying)
#define t1 (mutex[1]@trying)
#define t2 (mutex[2]@trying)
#define l0 (_last == 0)
#define l1 (_last == 1)
#define l2 (_last == 2)
#define f0 ([] <> l0)
#define f1 ([] <> l1)
#define f2 ([] <> l2)

ltl p1 { []   !(c0  &&  c1)  && !(c0 && c2) && !(c1 && c2)}
ltl p2 { []((t0 || t1 || t2) -> <> (c0 || c1 || c2)) }
ltl p3 {
        (f0 -> [](t0 -> <> c0))
        &&
        (f1 -> [](t1 -> <> c1))
        &&
        (f2 -> [](t2 -> <> c2))
        };

在您的代码中,您应该为每组 3 相关线程使用不同的 lock 变量。锁争用仍然会在全局级别发生,但是在 临界区 内工作的某些进程不会导致其他进程等待,但属于同一线程组的进程除外。

另一个想法是利用通道实现互斥:让每组线程共享一个公共最初包含一个 token 消息的异步 通道。每当这些线程之一想要访问临界区时,它就会从通道中读取。如果 token 不在频道内,它会等待直到可用。否则,它可以在关键部分继续前进,并在完成后将 token 放回 共享通道 中。

proctype node (chan inp; byte ppid)
{
   chan lock = [1] of { bool };
   lock!true;

   run recv_A(lock);
   run send_B(lock);
   run do_C(lock);
};

proctype recv_A(chan lock)
{
    bool token;
    do
        :: true ->

            // non-critical section code
            // ...

            // acquire lock
            lock?token ->

            // critical section
            // ...

            // release lock
            lock!token

            // non-critical section code
            // ...
    od;
};

...

这种方法可能是最简单的开始,所以我会先选择这种方法。但是请注意,我不知道这会如何影响验证期间的性能,这很可能取决于 Spin 内部处理通道的方式。可以在文件 channel_mutex.pml.

中找到此解决方案的完整代码示例 here

总而言之,请注意您可能想要添加 互斥进步锁定自由 LTL 属性添加到您的模型,以确保其行为正确。这些属性的定义示例可用 here and a code example is available here.