用信号量进程同步与互斥

1.理解生产者和消费者问题

没有引入信号量时的生产者和消费者进程,什么情况下会出现结果不唯一?什么情况下会出现永远等待?

用信号解决生产者和消费者的同步与互斥,要求能自己写出来。

答:sleep()和wakeup()是操作系统基本内核函数,他们被封装在函数库中供应用程序使用,执行sleep()时,让调用进程进入等资源队列,当资源可用时从相应队列释放等资源的进程。

结果不唯一:当进程之间共享变量counter,对counter的访问未加限制,生产者进程和消费者进程对counter的交替操作会使其结果不唯一,例如counter的当前值为8,如果生产者生产一件产品并投入缓冲区,拟执行counter加1操纵,同时消费者获取一个产品消费,拟执行counter减1操作,假如两者交替执行加1或减1操作,取决于其执行速度,counter的值可能是9,也可能是7,但是其正确值应该是8.

出现永远等待:假定消费者读取counter值时发现它为0,此时调度程序暂停消费者进程,让生产者进程运行,生产者加入一个产品,将counter的值加1,他想当然的推想由于counter值刚才为0,所以此时消费者一定是在睡眠,于是生产者调用wakeup()函数来唤醒消费者,遗憾的是,消费者尚未睡眠时唤醒信号丢失,当消费者下次运行时,因已测到counter的值为0,于是去睡眠,这样生产者迟早会填满缓冲区然后去睡眠,这样就造成了所有进程都永远处于睡眠状态。

PV操作:

item B[k];

semaphore empty;empty=k;

semaphore full;full=0;

semaphore mutex;mutex=1;

int in=0;

int out=0;

cobegin

  process producer_i)(){

    while(ture){

      produce();

      P(empty);

      P(mutex);

      apppend to B[in];

      in=(in+1)%k;

      V(mutex);

      V(full);

}

process consumer_j(){

  while(ture){

    P(full);

    P(Mutex);

    take from B[out];

    out=(out+1)%k;

    V(muter);

    V(empty);

    consume();

  }

}

}

2.哲学家吃面问题

semaphore fore[5];
for (int i=0;i<5;i++)
{

  fork[i]=1;
}

cobegin

process philosopher_i(){

  while(ture){

    think();

    P(fork[i]);

    P(fork[(i+1)%5]);

      eat();

    V(fork[i]);

    V(fork[(i+1)%5]);

  }

}

coend

分析:显然,最多两个哲学家同时吃面,假如每个哲学家都拿起一只筷子,就会永久等待(死锁),下面介绍一种方法。

typedef int Semaphore;       //定义信号量Semaphore类型是整型的别名
const int N = 5;             //定义哲学家的数目,该数目可以根据需要修改
const LEFT (i-1)%N;          //哲学家i的左邻
const RIGHT (i+1)%N;         //哲学家i的右邻
enum status{Thinking,Hungry,Eating};//哲学家所处的三种状态的常量枚举  
Semaphore mutex = 1;         //定义一个互斥信号量,实现对临界区的互斥
semaphore s[N] = {0};        //每个哲学家对应的信号量,初值为0
int state[N];                //每个哲学家的状态数组

  

void TakeChopstick(int i)    //第i个哲学家试图拿筷子
{
    P(mutex);                //进入访问共享变量state[i]的临界区前对mutex的P操作 
    state[i]=Hungry;         //设置状态为饥饿
    test(i);                 //测试能不能取筷子
    V(mutex);                //起开临界区
    P(s[i]);                 //如果没有筷子,进程阻塞
}

  

void PutChopstick(int i)     //哲学家i放下筷子
{ 
    P(mutex);                //访问临界区的互斥信号量
    State[i]=Thinking;       //吃完了,放下筷子进入思考状态
    test(LEST);              //唤醒左边哲学家拿筷子
    test(RIGHT);             //唤醒右边哲学家拿筷子
    V(mutex);                //离开临界区
}

  

void test(int i)
{
    if(state[i]==Hungry && state[LEFT] != Eating &&state[RIGHT] !=Eating)
    {
        State[i]=Eating;
        V(s[i]);        //唤醒哲学家进程,唤醒当前被阻塞的进程
    }
}

  

process Phliospher(int i)
{
    while(1)
    {
        思考;
        TakeChopstick(i);
         吃面条;
         PutChopstick(i);
    }
}

  3.读写文件问题

单纯使用信号量不能解决此此问题,必须引入计数器readcount对读进程计数,mutex是用于对计数器readcount操作的互斥信号量,writerblock表示是否允许写的信号量。

int readcount=0;

semaphore writeblock,mutex;

writeblock=1;mutex=1;

cobegin

process reader_i(){        process writer_j(){

  P(mutex);            P(writeblock);

  readcount++;           V(writeblock);

  if(readcount==1)       }

    P(writeblock);

    V(mutex);

    P(mutex);

    readcount--;

    if(readcount==0)

      V(writeblock);

    V(mutex);

}

coend

 

4.理发师问题

int waiting =0;

int CHAIRS=N;

semaphore customers,barbers,mutex;

customers=0;barber=0;mutex=1;

cobegin

  process barbers(){

    while(ture){

      P(customers);

      P(mutex);

      waiting--;

      V(barber);

      V(mutex);

      cutair();

    }

}

process customer_i(){

  P(mutex);

  If(waiting<CHAIRS){

    waiting++;

    V(customers);

    V(mutex);

    P(barbers);

    get_haircut();

}

else

    V(mutex);

}

coend

第一队音乐爱好者要竞争“待出售的音乐磁带和电池”,而且在初始状态下,系统并无“待出售的音乐磁带和电池”,故可为该种资源设置一初值为0的信号量buy1,同样,需设置初值为0的buy2、buy3分别对应“待出售的随声听和电池”、"待出售的随声听和音乐磁带"。另外,为了同步买者的付费动作和给货动作,还需设置信号量payment和goods,以保证买者在付费后才能得到所需商品,信号量music overi用来同步音乐爱好者听乐曲和酒吧老板的下一次出售行为,具体如下:

semaphore buy1=buy2=buy3=0;

semaphore payment=0;

semaphore goods=0;

swmaphore music_over=0;

cobegin{

process boss(){//酒吧老板

while(ture){

拿出任意两种物品出售;

if(出售的是音乐磁带和电池) V (buy1);

else if(出售的是随声听和电池) V(buy2);

else if(出售的是随声听和音乐磁带) V(buy3);

P(payment);//等待支付

V(goods);//给货

P(music_over);//等待乐曲结束

}

}

process fan1(){//第1队音乐爱好者

while(ture){//因为一个进程代表一队,而不是一个爱好者,所以这里是//while(ture)

P(buy1);//等有音乐磁带和电池出售

V(payment);//付费

P(goods);//取货

欣赏一曲乐曲;

V(music_over);//通知老板乐曲结束

}

}

process fan2(){//第二队音乐爱好者

while(ture){

P(buy2);  //等有随声听和电池出售

V(payment);//付费

P(goods);//取货

欣赏一曲乐曲;

V(music_over);//通知老板乐曲结束

}

}

process fan3(){//第三队音乐爱好者

while(ture){

P(buy3);  //等有随声听和音乐磁带出售

V(payment);//付费

P(goods);//取货

欣赏一曲乐曲;

V(music_over);//通知老板乐曲结束

}

}

coend

6.某银行有人民币储蓄业务,由n个储蓄员负责。每个顾客进入银行后先取一个号,并且等着叫号。当一个储蓄人员空闲下来,就叫下一个号。请用P,V操作正确编写储蓄人员和顾客进程的程序。

var customer_counter,mutex:semaphore;

    customer_counter:=0;

  mutex:=1;

cobegin

  process customer

    begin

      L1:take a number;

        P(mutex0);

          进入队列;

        V(mutex);

        V(customer_count);

        go to L1;

    end

  Process serversi(i=1,2,3...)

    begin

      P(cunstom_count);

      P(mutex);

      从队列区号;

      V(mutex);

      为该号客人服务;

      end;

coend;

8.九、在一个盒子里,混装了相等数量的黑棋子和白棋子,现要用自动分拣系统把黑棋子和白棋子分开,该系统由两个并发执行的进程P1和P2组成,其中进程P1专门拣黑子,进程P2专门拣白子。规定两个进程轮流拣子且每个进程每次只拣一个子。当一个进程在拣子时不允许另一个进程去拣子,并设P1先拣。请用P,V操作管理这两个并发进程,使其能正确实现上述功能

var S1,S2:semaphore;

S1:=1;S2:=0;

cobegin

{

process P1

begin

repeat

P(S1);

拣白子

V(S2);

until false;

end

process P2

begin

repeat

P(S2);

拣黑子

V(S1);

until false;

end

}

coend.

posted @ 2019-05-06 19:45  宋万里  阅读(1103)  评论(0编辑  收藏  举报