睡眠和唤醒
下面我们来看看睡眠和醒来的基本模型。假设有两个系统调用作为睡眠和唤醒。呼叫睡眠的过程将被阻止,而调用的过程将被唤醒。
有一个叫做生产者消费者问题的流行例子,它是模拟睡眠和唤醒机制的最流行的问题。
睡眠和觉醒的概念非常简单。如果关键部分不是空的,那么该过程将进入休眠状态。它将被临界区内正在执行的其他进程唤醒,以便进程可以进入临界区。
在生产者消费者问题中,让我们说有两个过程,一个过程写某事,而另一个过程读取。正在写东西的过程称为生产者,而正在阅读的过程称为消费者。
为了读取和写入,它们都使用缓冲区。下面显示了为生产者消费者问题提供解决方案的模拟睡眠和唤醒机制的代码。
#define N 100 //maximum slots in buffer
#define count=0 //items in the buffer
void producer (void)
{
int item;
while(True)
{
item = produce_item(); //producer produces an item
if(count == N) //if the buffer is full then the producer will sleep
Sleep();
insert_item (item); //the item is inserted into buffer
countcount=count+1;
if(count==1) //The producer will wake up the
//consumer if there is at least 1 item in the buffer
wake-up(consumer);
}
}
void consumer (void)
{
int item;
while(True)
{
{
if(count == 0) //The consumer will sleep if the buffer is empty.
sleep();
item = remove_item();
countcount = count - 1;
if(count == N-1) //if there is at least one slot available in the buffer
//then the consumer will wake up producer
wake-up(producer);
consume_item(item); //the item is read by consumer.
}
}
}
生产者生产该项目并将其插入缓冲区。每次插入时全局变量计数的值都会增加。如果缓冲区被完全填满并且没有插槽可用,那么生产者将会休眠,否则它将继续插入。
在消费者方面,每次消费时,计数值都降低了1。如果缓冲区在任何时间点都是空的,那么消费者就会进入休眠状态,它会持续消耗这些物品并将计数值减1。
如果缓冲区中至少有1个物品可用,消费者将被生产者唤醒。如果缓冲区中至少有一个可用槽位,生产者将被消费者唤醒,以便生产者可以写入。
那么,这个问题就出现在消费者即将休眠之前被抢占的情况下。现在消费者既不休眠也不消费。由于生产者没有意识到消费者实际上并不在休眠的事实,因此它不断地唤醒消费者,而消费者因为没有休眠就会响应。
这导致了系统调用的浪费。当消费者重新安排时间时,它会因为即将被抢占而休眠。
生产者不停地写入缓冲区,并在一段时间后得到填充。 生产者也会在那个时候休眠,记住当缓冲区中有一个插槽时,消费者会唤醒他。
消费者也在休眠,并且不知道生产者会把他叫醒。
这是一种僵局,无论生产者还是消费者都活跃起来,彼此等待唤醒他们。 这是一个需要解决的严重问题。
使用标志位来消除问题
标志位可以用来消除这个问题。 生产者可以在第一次唤醒时设置该位。 当消费者安排时,它会检查这个标志位。
消费者现在会知道生产者试图唤醒他,因此它不会休眠并进入准备好的状态,以消费生产者产生的任何东西。
这种解决方案仅适用于一对生产者和消费者,如果有n个生产者和n个消费者,该怎么办? 在那种情况下,需要保持一个整数,该整数可以记录多少次唤醒呼叫以及有多少消费者不需要睡眠。 这个整数变量被称为信号量。 稍后我们将详细讨论关于信号量的更多信息。