operating-system - 为什么以这种方式在信号量方面实现监视器?

标签 operating-system synchronization computer-science semaphore monitor

我从操作系统概念的信号量方面难以理解监视器的实现

5.8.3使用信号量实现监视器

我们现在考虑监视机制的可能实现
使用信号量。

为每个监视器提供了信号量互斥锁(初始化为1)。一个
进程必须在进入监视器之前执行wait(mutex),并且必须
离开显示器后执行signal(mutex)。

由于信令过程必须等到恢复的过程离开或等待为止,因此引入了另一个信号量next
初始化为0。信令过程可以使用next挂起
他们自己。还提供了一个整数变量next_count进行计数
next上挂起的进程数。因此,每个外部
函数F被替换为

wait(mutex);
...
body of F
...
if (next count > 0)
    signal(next);
else
    signal(mutex);

确保在监视器内相互排斥。

现在我们可以描述以及如何实现条件变量。
对于每种条件x,我们引入一个信号量x_sem和一个
整数变量x_count,都初始化为0。现在可以将x.wait()操作实现为
x_count++;
if (next_count > 0)
    signal(next);
else
    signal(mutex);
wait(x sem);
x_count--;

可以将x.signal()操作实现为
if (x_count > 0) {
    next_count++;
    signal(x_sem);
    wait(next);
    next_count--;
}

引入信号量next和暂停在next_count上的进程的计数next的原因是什么?

为什么按原样实现x.wait()x.signal()

谢谢。

最佳答案

-------注意-------

WAIT() SIGNAL()表示对监视器方法的调用
wait() signal()表示对信号量方法的调用,在以下说明中。

-------注释结尾-------

我认为如果您举一个具体的例子,会更容易。但是在此之前,让我们首先尝试了解什么是监视器。如书中所述,监视器抽象数据类型,意味着它不是可用于实例化变量的真实类型。相反,它就像是具有一些规则和准则的规范,基于这些规则和准则,不同的语言可以为进程同步提供支持。

信号量是作为基于软件的解决方案而引入的,用于在基于硬件的方法(如TestAndSet()或Swap())上实现同步。即使有信号灯,程序员也必须确保以正确的顺序正确地调用 wait()和signal()方法。因此,引入了一个称为监视器的抽象规范,将与同步相关的所有这些内容封装为一个原语,因此,在监视器内执行的任何进程都将确保相应地使用这些方法(信号灯等待和信号)。

使用监视器时,所有共享变量和函数(使用共享变量)都将放入监视器结构,并且在调用这些函数中的任何一个时,监视器实现均应确保确保共享资源受到保护,以防止相互排斥和任何同步问题。

现在,使用监视器来监视信号量或其他同步技术,我们不再只处理关键部分的一部分,而是使用不同的函数来处理其中的许多部分。此外,我们还具有在这些函数中访问的共享变量。对于监视器中的每个不同功能,以确保仅执行其中一个功能,并且不对任何一个功能执行其他进程,我们可以使用称为互斥体的全局信号量。

考虑下面使用监视器解决餐饮哲学家问题的解决方案示例。

monitor dining_philopher
{
     enum {THINKING, HUNGRY, EATING} state[5];
     condition self[5];

     void pickup(int i) {
         state[i] = HUNGRY;
         test(i);

         if (state[i] != EATING)
             self[i].WAIT();
     }

     void putdown(int i) {
         state[i] = THINKING;
         test((i + 4) % 5);
         test((i + 1) % 5);
     }

     void test(int i) {
         if (
              (state[(i + 4) % 5] != EATING) &&
              (state[i] == HUNGRY) &&
              (state[(i + 1) % 5] != EATING)) 
         {
                  state[i] = EATING;
                  self[i].SIGNAL();
         }
     }

     initialization code() {
          for (int i = 0; i < 5; i++)
              state[i] = THINKING;
          }
     }
}

理想情况下,流程如何调用这些功能应按以下顺序进行:

DiningPhilosophers.pickup(i);
...
  // do somework
...
DiningPhilosophers.putdown(i);

现在,虽然在 Pickup()方法内部执行一个进程,但另一个进程可能尝试调用 putdown()(甚至是Pickup)方法。为了确保互斥,我们必须确保在任何给定时间仅在监视器内部运行一个进程。因此,要处理这些情况,我们有一个全局信号量互斥量,它封装了所有可调用(拾取和放下)方法。因此,这两种方法将实现如下:

     void pickup(int i) {
         // wait(mutex);

         state[i] = HUNGRY;
         test(i);

         if (state[i] != EATING)
             self[i].WAIT();

         // signal(mutex);
     }

     void putdown(int i) {
         // wait(mutex);

         state[i] = THINKING;
         test((i + 4) % 5);
         test((i + 1) % 5);

         // signal(mutex);
     }

现在,只有一个进程能够以其任何方法在监视器内部执行。现在,通过此设置,如果Process P1 已执行 Pickup()(但尚未将tp 放下筷子),然后处理 P2 (例如相邻的小餐馆),则尝试 Pickr(因为)他/她的筷子(共享资源)正在使用中,必须使用 wait()才能使用。让我们看看监视器的条件变量的 WAIT SIGNAL 实现:

WAIT(){
    x_count++;

    if (next_count > 0)
        signal(next);
    else
        signal(mutex);

    wait(x_sem);
    x_count--;
}

SIGNAL() {
    if (x_count > 0) {
        next_count++;
        signal(x_sem);
        wait(next);
        next_count--;
    }
}

条件变量的WAIT实现与信号量的实现不同,因为它必须提供更多的功能,例如允许其他进程通过释放互斥量全局信号量来调用监视器的功能(等待时)。因此,当 P2 Pick()方法调用WAIT时,它将调用 signal(mutex),从而允许其他进程调用监视方法,并针对特定于信号量的信号调用 wait(x_sem)有条件的。现在, P2 在这里被阻止了。另外,变量 x_count 会跟踪等待条件变量(自身)的进程数。

因此,当 P1 调用 putdown()时,这将通过 test()方法调用SIGNAL。在SIGNAL内,当 P1 在其持有的筷子上调用信号(x_sem)时,它必须另外做一件事。它必须确保监视器中仅运行一个进程。如果只调用信号(x_sem),则从那时起 P1 P2 都将开始在显示器内部进行操作。为防止此 P1 ,在释放其筷子后,它将自行阻塞,直到 P2 完成。为了阻止自身,它使用信号量和下一个。并使用计数器 next_count 来通知 P2 或其他进程有人被阻止。

因此,现在 P2 将得到筷子,并且在退出 Pickup()方法之前,它必须释放正在等待 P2 完成的 P1 。因此,现在,我们必须按以下方式更改 Pickup()方法(以及监视器的所有功能):

     void pickup(int i) {
         // wait(mutex);

         state[i] = HUNGRY;
         test(i);

         if (state[i] != EATING)
             self[i].WAIT();

         /**************
         if (next_count > 0)
             signal(next);
         else
             signal(mutex);
         **************/
     }

     void putdown(int i) {
         // wait(mutex);

         state[i] = THINKING;
         test((i + 4) % 5);
         test((i + 1) % 5);

         /**************
         if (next_count > 0)
             signal(next);
         else
             signal(mutex);
         **************/
     }

因此,现在,在任何进程退出监视器功能之前,它会检查是否有任何等待的进程,如果有,则释放它们,而不是互斥体全局信号量。最后的此类等待进程将释放互斥锁信号量,允许新进程进入监视功能。

我知道这很长,但是我花了一些时间来理解并希望将其写成书面形式。我将很快在博客上发布它。

如果有任何错误,请通知我。

最好,
沙比尔

关于operating-system - 为什么以这种方式在信号量方面实现监视器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46919797/

相关文章:

windows - 即使驱动程序没有完成irp,同步I/O是否可以自动取消?

c - 使线程获得相等的时间片

python - 根据名称中的某些值动态创建目录和文件

windows - 需要从 32 位和 64 位机器 Windows 机器读取注册表值

version-control - 在家工作时使用什么策略来同步代码

mysql 导入/导出

math - 摄像机平移向量-与旋转矩阵的关系

plugins - 修复错误 : Plugin with id 'com.android.application' not found

java - 为允许拼写多个单词的单词搜索游戏选择随机字母的算法

ios - Swift 中的 16 位逻辑/计算机模拟