我无法从操作系统概念
的信号量理解监视器的实现5.8.3使用信号量实现监视器
我们现在考虑监控机制的可能实现 使用信号量。
对于每个监视器,提供信号量互斥(初始化为1)。一个 进程必须在进入监视器之前执行wait(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()
按原样实施?
感谢。
答案 0 :(得分:3)
-------注意-------
WAIT()和 SIGNAL()表示对监视器方法的调用
在以下说明中, wait()和 signal()表示对信号量方法的调用。
-------注释结尾-------
如果您以具体示例的方式考虑,我认为这会更容易。但是在此之前,让我们首先尝试了解什么是监视器。正如本书中所解释的,监视器是抽象数据类型,这意味着它不是可用于实例化变量的实类型。相反,它就像是带有一些规则和准则的规范,基于该准则,不同的语言可以为流程同步提供支持。
引入了信号量作为基于软件的解决方案,用于通过 TestAndSet()或Swap()等基于硬件的方法实现同步。即使有信号灯,程序员也必须确保以正确的顺序正确地调用 wait()和signal()方法。因此,引入了一个称为 monitors 的抽象规范,将与同步相关的所有这些事情封装为一个原语,因此,在监视器内部执行的任何进程只需确保这些方法(信号量等待和信号)< / em>调用相应地使用。
对于监视器,所有共享变量和功能(使用共享变量的功能)都将放入监视器结构,并且当调用这些功能中的任何一个时,监视器实现将确保确保共享资源免受相互排斥和任何问题的影响同步。
现在有了监视器,与信号量或其他同步技术不同,我们不仅仅处理关键部分的一部分,而是根据不同功能处理其中的许多部分。此外,我们还具有在这些函数中访问的共享变量。对于监视器中的每个不同功能,要确保仅执行其中一个功能,并且不对任何一个功能执行其他进程,可以使用名为 mutex 的全局信号量。
使用下面的监视器来考虑解决哲学家问题的示例。
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() (甚至是拾取)方法。为了确保互斥,我们必须确保在任何给定时间在监视器中仅运行一个进程。因此,要处理这些情况,我们有一个全局信号量 mutex ,该信号量封装了所有可调用的(pickup&putdown)方法。因此,这两种方法将实现如下:
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);
}
现在,只有一个进程能够以其任何方法在监视器内部执行。现在,使用此设置,如果进程 P1 已经执行了 pickup() (但还没有tp putdown 筷子)然后处理 P2 (例如相邻的小餐馆)尝试 pickup():因为他/她的筷子(共享资源) 正在使用中,它必须 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实现与信号量的实现不同,因为它必须提供更多功能,例如允许其他进程通过释放 mutex来调用监视器的功能(等待)。 全局信号量。因此,当 P2 从 pickup()方法调用WAIT时,它将调用 signal(mutex),从而允许其他进程调用监视器方法并在特定于条件的信号量上调用 wait(x_sem)。现在, P2 在此处被阻止。此外,变量 x_count 跟踪条件变量(self)上等待的进程数。
因此,当 P1 调用 putdown()时,这将通过 test()方法调用SIGNAL。当 P1 在其持有的筷子上调用 signal(x_sem)时,在SIGNAL内部,它必须做另外一件事。它必须确保监视器中仅运行一个进程。如果只调用 signal(x_sem),则从那时起 P1 和 P2 都将在显示器内部开始工作。为防止此 P1 ,松开筷子后,它会自行阻塞,直到 P2 完成。为了阻止自身,它使用信号灯 next 。并使用计数器 next_count 通知 P2 或其他进程有人被阻止。
因此,现在 P2 将得到筷子,并且在退出 pickup()方法之前,它必须释放正在等待< strong> P2 完成。因此,现在,我们必须更改 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);
**************/
}
因此,现在,在任何进程退出监视器功能之前,它会检查是否有任何等待的进程,如果有,则释放它们,而不是 mutex 全局信号灯。这样的最后一个等待进程将释放 mutex 信号量,允许新进程进入监视功能。
我知道这很长,但是我花了一些时间来理解并希望将其写成文字。我会尽快将其发布在博客上。
如果有任何错误,请告诉我。
最佳,
沙比尔
答案 1 :(得分:2)
我同意这令人困惑。
让我们先了解第一段代码:
// if you are the only process on the queue just take the monitor and invoke the function F.
wait(mutex);
...
body of F
...
if (next_count > 0)
// if some process already waiting to take the monitor you signal the "next" semaphore and let it take the monitor.
signal(next);
else
// otherwise you signal the "mutex" semaphore so if some process requested the monitor later.
signal(mutex);
回到你的问题:
接下来介绍信号量和计数的原因是什么? next_count进程暂停在下一个意味着什么?
假设您有一个正在执行某些I / O的进程,它需要被阻塞直到它完成。所以你让其他进程在就绪队列中等待获取监视器并调用函数F.
next_count 仅用于跟踪队列中等待的进程。
暂停在下一个信号量上的进程是在条件变量上发出等待的进程,因此它将被暂停,直到其他一些 进程(下一个进程)将其唤醒并恢复工作。
为什么x.wait()和x.signal()以它们的方式实现?
让我们走 x.wait():
semaphore x_sem; // (initially = 0)
int x_count = 0; // number of process waiting on condition (x)
/*
* This is used to indicate that some process is issuing a wait on the
* condition x, so in case some process has sent a signal x.signal()
* without no process is waiting on condition x the signal will be lost signal (has no effect).
*/
x_count++;
/*
* if there is some process waiting on the ready queue,
* signal(next) will increase the semaphore internal counter so other processes can take the monitor.
*/
if (next_count > 0)
signal(next);
/*
* Otherwise, no process is waiting.
* signal(mutex) will release the mutex.
*/
else
signal(mutex);
/*
* now the process that called x.wait() will be blocked until other process will release (signal) the
* x_sem semaphore: signal(x_sem)
*/
wait(x_sem);
// process is back from blocking.
// we are done, decrease x_count.
x_count--;
现在让我们采取 x.signal():
// if there are processes waiting on condition x.
if (x_count > 0) {
// increase the next count as new blocked process has entered the queue (the one who called x.wait()). remember (wait(x_sem))
next_count++;
// release x_sem so the process waiting on x condition resume.
signal(x_sem);
// wait until next process is done.
wait(next);
// we are done.
next_count--;
}
如果您有任何问题,请发表评论。