進程對臨界資源的互斥訪問


臨界資源與臨界區

臨界資源(critical resource):一次只能供一個進程使用的資源。   如:硬件有打印機等,軟件有變量,磁盤文件(寫入的時候)。

臨界區(critical section):把進程中訪問臨界資源的那段代碼成為臨界區。

為了實現臨界資源的互斥訪問,只要做到進程互斥地進去自己的臨界區,便可以實現進程對臨界資源的互斥訪問。

同步機制

為實現各進程互質地訪問自己的臨界區,操作系統需要同步機制來協調各進程的運行。

1、同步機制的規則

  (1)空閑讓進:當無進程處於臨界區,表明臨界資源處於空閑狀態,允許請求進去臨界區的進程進去臨界區

  (2)忙則等待:當有進程處於臨界區,表明臨界資源正在訪問,其他請求進入臨界區的進程必須等待。

  (3)有限等待:應使在等待的進程在有限的事件內進入自己的臨界區,避免"死等"狀態

  (4)讓權等待:當進程不能進入自己的臨界區,應立即釋放處理機,以避免進入"忙等"狀態

2、硬件同步機制

 為臨界資源設置一把鎖,當臨界資源無進程使用時,鎖是打開的,當臨界資源正在被進程使用時,則鎖是關閉的。

鎖可能是一個布爾變量lock,初始時,lock = FALSE,那么每次訪問臨界資源時,都要進程鎖測試。

bool lock = false;
    if(!lock)
    {
        lock = true;
        access critical section;//訪問臨界區資源
    }

但是在多任務的處理機中,可能會發生錯誤。假設有2個進程都要訪問同一臨界資源,進程a通過了鎖測試,但是未將lock設為true時,發生了進程調度,進程b開始執行鎖測試,同樣通過了鎖測試,從而使得兩個進程訪問同一個臨界資源。

  (1)關中斷:在進入鎖測試之前關閉中斷,直到完成鎖測試並且上鎖之后才打開中斷,但是這樣很不好,限制了CPU處理任務的能力,而且也很危險,如果用戶在中斷后寫個死循環,那不就死機了。

  (2)利用Test-and-set指令:這是一條硬件指令,且該指令是原子操作(atomic operataion),原子操作指的是該執行過程要么不做,那么全做,即像原子一樣不可分割。下面的TS就是一個原子操作,所以不會發生上面所說的問題。

bool TS(bool *lock)//lock 為臨界資源的鎖
{
    bool old ;
    old = lock;
    lock = true;//如果鎖為true,那么這句不影響鎖,如果鎖為false,那么正好上鎖
    return old;
}
while(TS(&lock));//循環測試直到TS(&lock) 為false
access critical section;
lock = false;

  (3)利用Swap指令:該指令也是硬件指令,且是原子操作。可以看出下面的代碼其實思想是和上面的代碼思想是一樣的。只不過實現方式不同而已。

void swap(bool *a, bool *b)
{
    //如果a==true,那么swap語句不影響鎖,如果a==false,那么正好上鎖
    bool tmp
    tmp = *a;
    *a = *b;
    *b = tmp;
}
bool key = true;
do{
    swap(&lock,&key);
}while(key!=false)

 

3、信號量機制

  (1)整形信號量:用一個整型的信號量S來表示資源的個數,然后通過兩個原子操作來進行判斷,這兩個原子操作是wait(S)和singal(S),也叫做P和V

其實這些操作很容易想出來,但是你不能自己直接寫if-else來判斷,因為你的這些代碼可能不具備原子性,所以會發生錯誤。

wait(S)
{
    while(S<=0);
    S--;
}
singal(S)
{
    S++;
}


//使用wait(S)和singal(S)來做到互斥訪問
wait(S);
access critical section;
singal(S);

  (2)記錄型信號量:在整形信號量中,只要S<0,那么wait原子操作就會不停地進行測試,直到S>=0,這種不斷測試,知道滿足條件的情況叫做忙等待。這樣不如使得該進程進入阻塞狀態,等滿足條件時再喚醒該進程。而且這樣更有利於提高CPU的效率。 這就是記錄型信號量所做的事情。

wait原語所做的操作是將S->value--,如果減完后小於0,那么說明臨界資源不夠,因此調用block原語,將當前進程阻塞,並記錄到鏈表S->list中

singal原語所做的操作是將S->value++,如果加完后小於等於0,那么說明鏈表中S->list有阻塞的進程,因此調用wakeup原語喚醒該進程來獲得臨界資源。

//記錄型數據結構
typedef struct
{
    int value;
    struct process_control_block *list;
}semaphore;


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

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

 

其實硬件指令同步和軟件同步所做的操作都是一樣的,只是他們實現的方式不同而已,一個是硬件,一個是軟件。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM