天天看點

談談作業系統中的信号量與PV操作

在臨界區的排程原則中有:
  • 互斥使用
  • 有空讓進
  • 忙則等待
  • 有限等待
  • 擇一而入
  • 算法可行
在實際應用中,我們考慮對臨界區的管理有軟體算法,也有硬體設施,但是這些偏軟,偏硬的方法,或存在複雜、效率低下,或存在浪費CPU時間等問題。下面筆者将和大家談談一種新的同步工具:信号量和PV操作。

PV操作

PV操作是屬于原語操作,原語操作即是執行時是不可被打斷的,如原子一般不可再分,通過PV操作我們可以保證執行時不可打斷且信号量值的完整性。

  • P(s)

    :将信号量的值減一(

    s.value - 1

    )
    • 如果結果小于0(

      s.value < 0

      ),執行P操作的程序将被阻塞,放入到與s信号量有關的list所指向的隊列中
    • 如果結果大于0(

      s.value>=0

      ),則執行P操作的程序繼續執行。
  • V(s)

    :将信号量的值加一(

    s.value+1

    )
    • 若結果不大于0(

      s.value <= 0

      ),則執行V操作的程序從與信号量有關的list所指向的隊列中釋放一個程序,使其轉于就緒态,自己繼續執行;
    • 若結果大于0(

      s.value>0

      ),則執行V操作的程序繼續執行。
    PV操作的推論:
    • 若信号量

      s.value

      為正值,此值等于在封鎖之前對信号量s可施行的P操作數,即

      s

      所代表的實際可用的實體資源數。
    • 若信号量

      s.value

      為負值,其絕對值等于登記排列在s信号量隊列之中等待的程序個數,即恰好等于對信号量

      s

      實施P操作而被封鎖的并進入信号量

      s

      等待隊列的程序數
    • 通常P操作意味着請求一個資源,V操作意味着釋放一個資源,在一定條件下,P操作代表挂起程序的操作,V操作代表被喚醒被挂起的程序的操作。

信号量

在作業系統中将用信号量來表示實體資源的實體,與隊列有關。同時,須知:

信号量是一種變量類型,有如下兩個分量:

  • 信号量的值(value)
    • value>0,表示實際可用資源數
    • value=0,表示資源為0
    • value<0,表示正在等待資源的程序數
  • 信号量的指針:用于指向等待的程序

按用途來分,信号量分為兩種:

  1. 公用信号量:
    1. 聯系一組并發程序,相關程序可以在此信号量上做PV操作,初值為1,這個為1,是為了實作程序的一個互斥;
  2. 私有信号量:
    1. 聯系一組并發程序,僅允許此信号量所擁有的程序執行P操作,其他相關的程序可實施V操作,初值一般為0或正整數,在程序同步中常用。

按取值來分,又有如下兩種:

  • 二值信号量
    • 僅能取值為0或1,解決程序互斥
  • 一般信号量
    • 允許取值大于1,常用于解決程序同步。

信号量的資料結構以及PV操作的細節

typedef struct semaphore
{
    int value; //信号量的值
    struct pcb * list;//信号量的隊列指針
}

void P(semaphore s){
    s.value --; 
    if(s.value < 0) // 小于0 被阻塞
        sleep(s.list);
    
}
void V(semaphore s){
    s.value ++;
    if(s.value <= 0)
        wakeup(s.list); //小于等于0 從信号量隊列中釋放一個等待的程序,并轉為就緒态
}
           

信号量實作互斥

通過使用信号量和PV操作來管理并發程序進入臨界區的形式:

semaphore mutex;
mutex=1;
cobegin
    process Pi(){
    	P(mutex);
    /**臨界區*/
    	V(mutex);
}
           

當有程序在臨界區中時,mutex的值為0或者負值,否則mutex為1.因為隻能有一個程序的P操作能把mutex的值減0,進而能夠保證互斥操作

實際應用(C語言實作)

現在有以下場景:

有一家人,小明專門吃蘋果,小紅專門吃橘子,爸爸放蘋果,媽媽放橘子,盤子上面隻能有一個水果,而隻有相對應的水果孩子們(孩子專門吃其對應的水果)才能拿來吃,隻有當盤子空的時候,爸爸媽媽才能放水果,試用程序并發實作這個操作。

首先要思考的是,臨界資源是什麼?顯然,是盤子,盤子有多少種狀态?三種!分别是:

  • 盤子是空的
  • 盤子上有橘子
  • 盤子上有蘋果

當有多個程序并發時,我們要保證資源區在每時每刻都能保證:互斥使用、有空讓進、忙則等待、有限等待、擇一而入,算法可行。下面我将使用PV操作實作這個操作:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include<semaphore.h>


#define P sem_wait
#define V sem_post
#define orange &orange_
#define apple &apple_
#define empty &empty_real1


sem_t orange_;
sem_t apple_;
sem_t empty_real1;

int num=0;

void * mother(){
    while(num < 30)
    {

        P(empty);
        num++;
        printf("媽媽放橘子 %d\n",num);
        V(orange);

    }

}
void * father()
{
    while(num < 30)
    {
        P(empty);
        num++;
        printf("爸爸放蘋果 %d\n",num);
        V(apple);
    }

}
void * xiaoming()
{
    while(num < 30)
    {
        P(apple);
        num++;
        printf("小明吃蘋果 %d\n",num);
        V(empty);
    }

}
void * xiaohong(){
    while(num < 30)
    {
        P(orange);
        num++;
        printf("小紅吃橘子 %d\n",num);
        V(empty);
    }

}

int main()
{
    sem_init(apple,0,0); //初始化信号量的值為0,并且設定其為程序内的線程共享
    sem_init(orange,0,0);
    sem_init(empty,0,1); //初始信号量的值為1,表示剛開始的時候盤子是空的,資源可用


    pthread_t tid1;
    pthread_t tid2;
    pthread_t tid3;
    pthread_t tid4;

    pthread_create(&tid1,NULL,mother,NULL);
    pthread_create(&tid2,NULL,father,NULL);
    pthread_create(&tid3,NULL,xiaoming,NULL);
    pthread_create(&tid4,NULL,xiaohong,NULL);

    pthread_exit(0);

    return 0;
}

           

以上代碼的執行結果:

談談作業系統中的信号量與PV操作

這裡可能大家看到為啥

num

的值為啥會大于30,這時思考以下

num

的範圍,并且線上程等待的時候,是否

num

值已經被判斷過了了?即可得知答案。

好了相關内容就分享到這裡,如果你覺得有用的話可以給我點個贊,謝謝!

繼續閱讀