生产者——消费者问题是 多个进程因共享一个缓存区而产生的相互依赖问题 。具体来说,生产者进程往往要往共享缓存区中放内容、消费者进程从共享缓存中取内容, 当缓存区满的时候 ,生产者进程需要等待消费者进程取走内容, 当缓存区空的时候 ,消费者进程需要等待生产者放入东西。
这种进程间的合作/依赖关系在计算机系统中非常常见,比如磁盘驱动程序和上层应用通过共享缓存区来交换数据内容,以磁盘为例,磁盘驱动程序是生产者模型,要讲磁盘中读取的内容放到共享缓存区中,上层应用程序是消费者进程,要从共享缓存区中取走内容交给用户处理。
要解决
在这里基于信号的生产者——消费者解决方案中,定义了两个信号: empty和full
empty表示“空闲缓存器个数” 消费者进程用掉一个内存单元(item)时,即产生一个空闲缓冲区时,会向生产者进程发送一个empty信号。
full表示“缓存区中有内存单元” 所以生产者生产一个空闲单元,并产生内容以后向消费者进程发出full信号。
if(counter==BUFFER_SIZE)
如果缓存区已满 ,此时生产者进程就令自己进入阻塞状态等待信号empy。直到消费者进程消费了一个内存单元(item)以后,消费者才会发现counter变为BUFFER_SIZE-1
如果消费者消费了一个内存单元以后 发现counter== BUFFER_SIZE-1,消费者就给生产者发送一个empty。
特殊情况 :在缓存区满进来两个生产者进程,然后又进来一个消费者进程。
缓存区满的时候进来生产者进程P1, counter== BUFFER_SIZE条件成立,P1进入阻塞态等待信号empty
又进来一个生产者进程P2, counter== BUFFER_SIZE条件仍然成立,P2也进入拥塞状态等待信号empty。
现在又进来一个消费者C, C消费了一个内容单元(item),counter–判断条件counter== BUFFER_SIZE-1,条件成立。=,C会发生信号empty唤醒进程P1,到目前为止,进程间的同步关系是正确的,
C再循环执行一次, 又用掉一个单元(item),执行item–,判断条件counter== BUFFER_SIZE-1,由于此时counter=BUFFER=2,判断条件不成立,所以P2不会被唤醒,仍然出与拥塞太等待信号empty,
P1和P2两个进程在empty上睡眠等待,而counter只是BUFFER_SIZE,和只有一个P1等待睡眠等待empty时的counter数值一样。当然counter的语义仍然是正确的,因为counter记录了缓存区中Item的个数,因此无论只有P1等待还是有P1和P2两个进程等待,两种情况下缓存区中的Item数目都是BUFFER_SIZE。因此counter的语义信息太少,无法充分反映阻塞在empty信号上的进程个数信息而导致应该将P2唤醒却没有唤醒的情况。
信号量就是再信号上关联一个整数 可以根据这个整数来决定进程的阻塞或者唤醒。
现在,我们可以从信号量的观点出发来重新考虑信号empty了:信号empty用来表示生产者由于缓存区没有空闲单元而睡眠的情况,现在empty变成信号量了,作为信号量,empty要关联着以一个表达“量”的整数,显然这个量可以是阻塞在empty上的进程个数。
对于前面的例子。
如果P1拥塞在empty信号上, empty=1
当P2又被拥塞后, empy的数值加1变为2,表示有两个进程阻塞在empty上
C循环一次, 发现empty== 2,表示有两个进程用塞在empty上,唤醒其中一个将empty修改为1;
C再循环一次, 发现empty==1,表示有一个进程阻塞在empty上,再唤醒一个,现在empty=0了
如果C再循环一次, 此时不会唤醒任何进程,但empty变为-1,那么-1表示现在缓存区中有一个空闲单元。对于生产者者进程来说,缓存区中的空闲单元是生产者需要的资源,现在出现了一个空闲单元,即有了一个资源。
struct semaphore
{
int value;//信号量数值,用来记录资源的个数或进程个数
PCB *queue;//等待在该信号量上的进程队列
}
P(semaohore)
{
s.value--;
if(s.value<0)
sleep_on(s.queue);
}
V(semaphore s)
{
s.value++;
if(s.value<=0)
wake_up(s.queue);
}
生产者在缓存区满了以后会睡眠等待, 此处定义一个信号量,当这个信号量的数值为0时,生产者要进行P操作睡眠等待,所以此处定义的信号量用0来表示缓存区满。因此这个信号量表达的含义就应该是 缓存区单元的个数 所以可以命名为empty初始化为BUFFER_SIZE,生产者对empty进行P操作,而消费者对empty信号执行V操作
消费者进程在缓存区没有空闲单元是会被阻塞, 所以对应的信号量为0的时要表达出没有空闲单元这样的语义,故该信号量的含义就是 缓存器中的空闲单元数, 可以命名为full。初值为0。
由于时共享缓存区, 当某个进程进入共享缓存区进行修改时,其他进程不能使用缓存区,只能睡眠等待。有睡眠等待就是一个同步点,因此需要再定义一个信号同步量来实现这个同步点。由于只能让一个程序进行修改缓存区,所以这个信号量的初值应该为1,一旦某个进程进入以后就应该将其置位0(对应P操作)。此时当其他进程再向修改缓存区时,对该信号量的P操作会导致进程阻塞等待。根据语义,这个信号量的含义时互斥进入,将其命名为mutec,初值为1,