操作系统课程设计报告

发布时间 : 星期日 文章操作系统课程设计报告更新完毕开始阅读

{

sleep(); }

insert_item(item); // 将新数据项放入缓冲区

count = count + 1; // 计数器加 1 if (count == 1) // 表明插入之前为空, {// 消费者等待

wakeup(consumer); // 唤醒消费者

} } }

/* 消费者进程 */ void consumer(void) {

int item; // 缓冲区中的数据项 while(true) // 无限循环 {

if (count == 0) // 如果缓冲区为空,进入休眠

9

{

sleep(); }

item = remove_item();// 从缓冲区中取出一个数据项

count = count - 1; // 计数器减 1 if (count == M -1) // 缓冲区有空槽 { // 唤醒生产者

wakeup(producer); }

consume_item(item); // 打印出数据项 } }

这里对 count 的访问是有可能出现竞争条件的:缓冲区为空,消费者刚刚读取 count 的值为 0,而此时调度程序决定暂停消费者并启动执行生产者。生产者向缓冲区中加入一个数据项,count 加 1。现在 count 的值变成了 1.它推断刚才 count 为 0,所以此时消费者一定在休眠,于是生产者开始调用 wakeup(consumer) 来唤醒消费者。但是,此时消费者在逻辑上并没有休眠,所以 wakeup 信号就丢失了。当消费者下次运行时,它将测试先前读到的 count 值,发现为 0(注意,其实这个时刻 count 已经为 1 了),于

10

是开始休眠(逻辑上)。而生产者下次运行的时候,count 会继续递增,并且不会唤醒 consumer 了,所以迟早会填满缓冲区的,然后生产者也休眠,这样两个进程就都永远的休眠下去了。

使用信号量解决生产者-消费者问题,首先了解一下信号量吧,信号量是 E.W.Dijkstra 在 1965 年提出的一种方法,它是使用一个整型变量来累计唤醒的次数,供以后使用。在他的建议中,引入了一个新的变量类型,称为信号量(semaphore),一个信号量的取值可以为 0(表示没有保存下来的唤醒操作)或者为正值(表示有一个或多个唤醒操作)。

并且设立了两种操作:p 和 v(分别为一般化后的 sleep 和 wakeup,其实也是一般教科书上说的 P/V 向量)。对一个信号量执行 p 操作,表示检查其值是否大于 0,如果该值大于 0,则将其值减 1(即用掉一个保存的唤醒信号)并继续;如果为 0,则进程休眠,而且此时 p 操作并未结束。另外,就是检查数值,修改变量值以及可能发生的休眠操作都作为单一的,不可分割的 原子操作 来完成。

下面开始考虑用信号量来解决生产者-消费者问题了,不过在此之前,再次分析一下这个问题的本质会更清晰点:问题的实质在于发给一个(尚)未休眠进程(如上的消费者进程在只判断了 count == 0 后即被调度出来,还未休眠)的 wakeup 信号丢失(如上的生产者进程在判断了 count ==

11

1 后以为消费者进程休眠,而唤醒它)了。如果它没有丢失,则一切都会很好。

#define M 100 // 缓冲区中的槽数目

typedef int semaphore; // 信号量一般被定义为特殊的整型数据

semaphore mutex = 1; // 控制对临界区的访问 semaphore empty = M; // 计数缓冲区中的空槽数目 semaphore full = 0; // 计数缓冲区中的满槽数目

/* 生产者进程 */ void proceducer(void) {

int item; while(1) {

item = procedure_item();// 生成数据 p(&empty); // 将空槽数目减 1 p(&mutex); // 进入临界区

insert_item(item); // 将新数据放入缓冲区

v(&mutex); // 离开临界区 v(&full); // 将满槽的数目加 1

12

联系合同范文客服:xxxxx#qq.com(#替换为@)