信号量机制以及各种例题

信号量机制

1.整型信号量

整型信号量定义为一个用于表示资源数目的整型量,除了初始化外,只能由wait(S)和signal(S)这两个操作访问。wait又称为P操作,signal又称为V操作。

wait(S){
    while(S<=0);   /*do no-op*/
    S--;
}
signal(S){
    S++;
}    

在wait操作中,当S<=0,表示资源数目不够,这时就会一直在while循环中,并什么都不做,使得进程处于“忙等”状态。

wait和signal可以理解为对资源数目的-1和+1,一次wait操作表明要使用一个资源,一次signal操作表明归还一个资源或者产生一个资源。

2.记录型信号量

记录型信号量可以解决上述的“忙等”问题,不会一直停留在wait操作中,但是会出现多个进程等待访问同一临界资源的情况。为此除了表示资源数目的整型变量为value,还增加了一个进程链表指针list,用于链接上述的所有等待进程。

记录型数据结构

typedef struct{
    int value;
    struct process_control_block *list;
}semaphore;

wait和signal操作

wait(semaphore *S){
    S->value--;
    if(S->value<0) block(S->list);
}
signal(semaphore *S){
    S->value++;
    if(S->value<=0) wakeup(S->list);
}

在wait操作中,先自减,如果资源数目小于0,说明资源不够用,这时就要把该进程从运行状态block为阻塞状态 ,进程需要等待,但是该进程还是获取了需要的资源。此时S->value的绝对值表示在该信号量链表中已阻塞进程的数目。

在signal操作中,先自增即释放资源,如果资源数目小于等于0,则需要唤醒链表中的进程,把它从阻塞状态改变为就绪状态。

当S->value=1,表示只允许一个进程访问临界资源此时信号量转变为互斥信号量,用于进程互斥。

3.AND型信号量

前面两个信号量只能解决多个进程对应一种资源的情况,当多个进程需要多种资源,就需要用到AND型信号量。

死锁问题:

假设两个进程A和B,都需要访问互斥型信号量Dmutex=1和Emutex=1。

           process A:          process B:              

step1: wait(Dmutex);     wait(Emutex);

step2: wait(Emutex);     wait(Dmutex);

当A和B都进行完step1操作时,Dmutex=0,Emutex=0.进行step2操作时,Emutex=-1,Dmutex=-1,则A和B都被阻塞。

这时A和B就进入死锁状态。

AND型同步机制的基本思想:将进程在整个运行过程中需要的所有资源一次性全部分配给进程,待进程使用完后再一起释放。只要有一个资源未能分配给进程,其他所有可能为之分配的资源也不分配给它。

Swait(S1,S2,....Sn){
    while(TRUE){
        if(S1>=1&&S2>=1&&.....Sn>=1){
            for(i=1;i<=n;i++)Si--;
            break;
        }
        else{
            由于有资源未能分配给进程,其他所有资源都不分配给它
        }
    }
}
Ssignal(S1,S2,....Sn){
    while(TRUE){
        for(i=1;i<=n;i++){
            Si++;
        }
    }
}

4.信号量集

在上述操作中,wait和signal都只能对信号量+1和-1,也就一位置不能对临界资源进行多个单位的申请和释放。当需要N个单位的资源时,要进行N个wait操作,很低效,此外当所申请的资源数量低于某一个下限值,不予以分配,从而保证系统的安全性。

设该资源的分配下限值为ti,即最大申请值不能超过ti,对该资源申请量为di。当Si<ti,就不给分配,一旦允许分配,Si=Si-di。

Swait(S1,t1,d1,....Sn,tn,dn)

Ssignal(S1,d1,....Sn,dn)

特殊情况:

1.Swait(S,d,d)。此时在信号量集中只有一个信号量S,但允许它每次申请d个资源,当现有资源少于d时,不予分配。

2.Swait(S,1,1)。此时信号量集已经蜕化为一般的记录型信号量(S>1时)或互斥信号量(S=1时)。

3.Swait(S,1,0)。当S>=1时,允许多个进程进入某特定区;当S=0,将阻止任何进程进入特定区。

信号量的应用

1.利用信号量实现进程互斥

两个进程的互斥

设互斥信号量mutex=1,初值为1,取值范围为(-1,0,1)。当mutex=1时,表示两个进程皆未进入需要互斥的临界区;当mutex=0时,表示有一个进程进入临界区运行,另外一个必须等待,挂入阻塞队列;当mutex=-1,时,表示有一个进程正在临界区运行,另外一个进程因等待而阻塞在信号量队列中,需要被当前已经在临界区运行的进程退出时唤醒。

mutex=0和mutex=-1时都需要阻塞,但是mutex=-1时阻塞的进程需要被唤醒,即wakeup操作。

semaphore mutex=1;
    PA(){
        while(1){
            wait(mutex);
            临界区;
            signal(mutex);
            剩余区;
        }
    }
    PB(){
        while(1){
            wait(mutex);
            临界区;
            signal(mutex);
            剩余区;
        }
    }
    

在利用信号量机制实现进程互斥时应该注意,wait(mutex)和signal(mutex)必须成对地出现。缺少wait(mutex)将会导致系统混乱,不能保证对临界资源的互斥访问;而缺少signa(mutex)将会使临界资源永远不被释放,从而使因等待该资源而阻塞的进程小能被突。 

补充:

一个访问临界资源的循环进程描述如下:

while(TRUE){
    进入区
    临界区
    退出区
    剩余区
}

 在每个进程中访问临界资源的那段代码称为临界区(riticalsection)。显然,若能保证诸进程互斥地进入自己的临界区,便可实现诸进程对临界资源的互斥访问。为此,每个进程在进入临界区之前,应先对欲访问的临界资源进行检查,看它是否正被访问。如果此刻临界资源未被访问,进程便可进入临界区对该资源进行访问,并设置它正被访问的标志;如果此刻该临界资源正被某进程访问,则本进程不能进 入临界区。因此,必须在临界区前面增加一段用 于进行上述检查的代码,把这段代码称为进入区(enry section)。 相应地,在临界区后面也要加上一段称为退 出区(exit sction)的代码,用于将临界区正被访问的标志恢复为末被访间的标志。进程中除上述进入区、临界区及退出区之外的其它部分的代码在这里都称为剩余区。

 2.利用信号量实现前趋关系

var  a1,a2,a3,a4,a5,a6,a7:semaphore: =0;


begin


  parbegin


  s1:begin  s1;  V(a1)  ;  V(a2)  ;  end


  s2:begin  P(a1)  ;  s2  ;  V(a4)  ;  V(a5)  ;  end

  s3:begin  P(a1)  ;  s3  ;  V(a6)  ;  end

  s4:begin  P(a2)  ;  s4  ;  V(a6)  ;  end

  s5:begin  P(a2)  ;  s5  ;  V(a6)  ;  end

  s6:begin  P(a3)  ;  P(a4)  ;  P(a5)  ;   s6  ;  end

 看看就懂,不过多解释。

典型例题

1.生产者——消费者问题

该问题描述了两个共享固定大小缓冲区的线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。

1.利用记录型信号量解决生产者消费者问题

假定在生产者和消费者之间的公用缓冲池中具有n个缓冲区,这时可利用互斥信号量mutex实现诸进程对缓冲池的互斥使用;利用信号量empty和full分别表示缓冲池中空缓冲区和满缓冲区的数量。又假定这些生产者和消费者相互等效,只要缓冲池未满,生产者便可将消息送入缓冲池;只要缓冲池未空,消费者便可从缓冲池中取走一个消息。对生产者-消费者问题可描述如下:
 

int in=0, out=0;

item buffer[n];

semaphore mutex =l, empty=n, full=0;
void proceducer() {

    do {

        producer an item nextp;

        ......

        wait(empty);//申请一个空的缓冲区
        
        wait(mutex);//申请互斥信号量,不准其他进程进入

        buffer[in] =nextp;//产品送入

        in=(in+1) % n;//下一个生产者 

        signal(mutex);//释放互斥信号量.准许其他进程进入

        signal(full);//释放一个满的缓冲区

        }while(TRUE);
}

void consumer(){

    do {

        wait(full);//申请一个满的缓冲区

        wait(mutex);//申请互斥信号量,不准其他进程进入

        nextc= buffer[out];//产品拿出

        out =(out+l) % n;//下一个消费者

        signal(mutex);//释放互斥信号量.准许其他进程进入

        signal(empty);//释放一个空的缓冲区

        consumer the item in nextc;

        ...

        }while(TRUE);
}

void main {

    cobegin

        producer();

        consumer();

    coend
}


用于实现互斥的信号量必须成对出现。

对于empty和full的wait和signal操作,也需要成对出现,但它们分别处于不同的进程中。

2.利用AND信号量解决生产者——消费者问题

int in= 0, out= 0;

item buffer[n];

semaphore mutex = 1, empty = n, full= 0;
void producer{

    do{

        producer an item nextp;

        Swait(empty, mutex);

        buffer[in] = nextp;

        in=(in+1) % n;

        Ssignal(mutex, full);
       
        }while(TRUE);

void consumer(){

    do {

        Swait(full, mutex);

        nextc = buffer[out];

        out= (out+1)% n;

        Ssignal(mutex, empty);
        
        consumer the item in nextc;

        }while(TRUE);
}

只是把两个wait变为一个Swait,两个signal变为一个Ssignal 。

2.哲学家进餐问题

有五个哲学家,他们的生活方式是交替地进行思考和进餐,哲学家们共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五个碗和五支筷子,平时哲学家进行思考,饥饿时便试图取其左、右最靠近他的筷子,只有在他拿到两支筷子时才能进餐,该哲学家进餐完毕后,放下左右两只筷子又继续思考。

1.利用记录型信号量解决哲学家进餐问题

经分析可知,放在桌子上的筷子是临界资源,在一段时间内只允许一位哲学家使用。为了实现对筷子的互斥使用,可以使用一个信号量表示一只筷子,由这五个信号量构成信号量数组。其描述如下:

semaphore chopstick[5]={1,1,1,1,1}

所有信号量均被初始化为1,第i位哲学家的活动可以描述为:

do{
    wait(chopstick[i]);//拿左边筷子
    wait(chopstick[(i+1)%5)]);//拿右边筷子
    ...
    //eat
    ...
    signal(chopstick[i]);//放下左边筷子
    signal(chopstick[(i+1)%5]);//放下右边筷子
    ...
    //think
    ...
    }while(TRUE)

假如五位哲学家同时拿起左边的筷子时,就会使五个信号量chopstick 均为0:当他们再试图去拿右边边的筷子时,都将因无筷子可拿而无限期地等待。对于这样的死锁问题,可采取以下几种解决方法:
(1)至多只允许有四位哲学家同时去拿左边的筷子,最终能保证至少有一位哲学家能够进餐,并在用毕时能释放出他用过的两只筷子,从而使更多的哲学家能够进餐。
(2)仅当哲学家的左、右两只筷子均可用时,才允许他拿起筷子进餐。
(3)规定奇数号哲学家先拿他左边的筷子,然后再去拿右边的筷子:而偶数号哲学家则相反。按此规定,将是1、2号哲学家竞争1号筷子: 3、4号哲学家竞争3号筷子。即五位哲学家都先竞争奇数号筷子,获得后,再去竞争偶数号筷子,最后总会有一位哲学家能获得两只筷子而进餐。

semaphore chopstick[5]=(1, 1,1,1, 1);

void philosopher(int i)

while(true){

    if(¡ mod2 =0){//偶数哲学家,先右后左。

        wait (chopstick [(¡ + 1) mod 5]);

        wait (chopstick [ï]);

        eat();

        signal (chopstick [i);

        signal (chopstick[(i+ 1) mod 5]);
}

    else{//奇数哲学家,先左后右。

        wait (chopstick [i]);

        wait (chopstick [(i+ 1) mod 5]);

        eat;

        signal (chopstick [(i+1) mod 5]);

        signal (chopstick [i]);
        }
}

2.利用AND信号量机制解决哲学家就餐问题

利用AND信号量机制可以解决上述死锁

semaphore chopstick[5]= [1,1,1,1,1];

do{

    //think

    Swait(chopstick[(i+ 1)%5], chopstick[i]);

    //eat

    Ssignal(chopstick[(i+1)%5], chopstick[i]);
  
  }while[TRUE];

3.读者——写者问题

1.允许多个读者可以同时对文件执行读操作
2.只允许一个写者往文件写信息
3.任一写者在完成写操作之前不允许其他读者后写者工作
4.写者执行写操作前,应该让已有的读者或者写者全部退出 

等我有时间补充 

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值