1. 程式人生 > 其它 >作業系統 第二章 程序的描述與控制(下半部分:演算法理論篇))

作業系統 第二章 程序的描述與控制(下半部分:演算法理論篇))

技術標籤:作業系統筆記其他

同步機制

硬體同步機制
  1. 關中斷
    在進入鎖測試之前關閉中斷,直到完成鎖測試並上鎖才能開啟中斷。
  2. 利用Text-and-Set指令實現程序互斥
    lock=FALSE時候資源空閒,lock=TRUE的時候資源正在被使用。
/* TS指令的一般性描述 */
boolean TS(boolean *lock) {
    boolean old;
    old = *lock;
    *lock = TRUE;
    return old;
}

/* 利用TS指令實現互斥 */
do{
    …    
    while TS(&lock);   // 使用的時候就一直在這個true的迴圈裡走
    critical section;     // 
    lock = FALSE;      // 當操作開啟鎖的時候,
    remainder section;
} while(TRUE)

  1. 使用Swap指令實現程序互斥
/* swap指令的一般性描述 */
void swap(boolean *a, boolean *b)    // 交換
{
    boolean temp;
    temp = *a;
    *a = *b;
    *b = temp;
}

/* 利用swap指令實現互斥 */
Do {
    key = TRUE    // 讓key=True
    do { 
        swap(&lock, &key);   // 交換lock和key  也就是讓lock=true
    } while (key != FALSE);   // 如果key不是false,證明交換前lock不是false,也證明當初是鎖著的,那就一直迴圈鎖死。什麼時候解鎖了再說。
    critical section;
    lock = FALSE;
    remainder section;
} while(TRUE)

軟體方式
  • 訊號量機制
  1. 整型訊號量
  2. 記錄型訊號量
  3. AND型訊號量
  4. 訊號量集
整型訊號量
  • wait(s)和signal(s)兩個操作
// S表示資源數目的整型量S
/*wait(s)操作*/
wait(S){
	while (S≤0){  /*do no-op*/ };   
	S = S – 1;  // 表明有一個需要資源
}

/* signal(s)操作*/
 signal(S){
            S++;     //讓訊號量 +1 表明資源數增加了一個
}
// 這種方式無法做到讓權等待
記錄型訊號量
// 引入程序阻塞機制,解決忙等問題
// 增加了對阻塞程序的記錄
/*記錄型的資料結構*/
typedef struct{
	int value;				// 資源數目
	struct process_control_block *list;		// 等待程序連結串列
}

/*wait(s) 請求資源*/
wait(semaphore *S){
	S->value--;       // 資源數量 -1 表明當前需求了一個訊號量
	if(S->value < 0){ 	
		block(S->list);	// 如果資源數量少於0,那就把請求加入阻塞佇列
	}
}

/*signal操作 釋放資源*/   
signal(semaphore *S){
	S->value++;	// 釋放一個資源
	if(S->value <= 0){
		wakeup(S->list);		// 如果是負數證明還有阻塞佇列,開啟一下
	}
}

記錄型訊號量特點
  1. S.value的含義

    S.value = 1,這時候wait(S)一下就進入了阻塞狀態,保證了只允許一個資源訪問,為互斥狀態。
    且當S大於0,代表資源充足,S為負數,代表資源需求量。S為0,代表阻塞。

  2. 遵循讓權等待

  3. 資源訪問過程是原語,不存在執行一半這種情況。

AND型訊號量
  • 基本思想:將多次對多個訊號量申請修改為一次,用一個語句完成,要麼一次獲得所有,要麼一個也得不到
  • 目的:防止資源不同導致的死鎖。
訊號量集
  • 一次需要N個某類資源,需要進行N次wait(S)操作,這樣效果低下,且容易出現數量問題死鎖,
  • Swait(S,d,d)對訊號量S,允許申請d個,當自遠少於d個不予分配
  • Swait(S,1,1),等價於一般的記錄型訊號量或互斥訊號量
  • Swait(S,1,0)。低於0個不予分配,只能申請1個,相當於可控開關,可以做if語句用

訊號量的應用

利用訊號量實現程序的互斥
// 設有程式A和B,都需要同一個臨界區
semaphore mutex = 1;	// 互斥訊號量,這裡表示的是mutex->value = 1;
Pa(){
	while(1){
		wait(mutex){};
		// Pa in 臨界區
		signal(mutex);
		// Pa 其他操作
		
	}
}

Pb(){
	while(1){
		wait(mutex){};
		// Pb in 臨界區
		signal(mutex);
		// Pb 其他操作
	}
}
利用訊號量實現前驅關係
p1(){
	S1;
	signal(s);		// 釋放訊號量
}

p2(){
	wait(s);		// 必須等到p1釋放訊號量才能執行p2d的剩餘操作
	S2;
}

void main(){
	semaphore s;
	s.value = 0;
	cobegin()		//	 併發執行開始標記
		p1();
		p2();
	coend
}

// 這裡可以根據前趨圖實現更加複雜的前驅關係,
前驅關係邏輯

p1(){S1;signal(a);signal(b);}
p2(){wait(a);S2;signal(c);signal(d);}
p3(){wait(c);S3;signal(e);}
p4(){wait(d);S4;signal(f);}
p5(){wait(b);S5;signal(g);}
p6(){wait(e);wait(f);wait(g);S6;}
main() {
    semaphore a,b,c,d,e,f,g;
    a.value=b.value=c.value=d.value=e.value=0;
    f.value=g.value=0;
    cobegin
        p1();p2();p3();p4();p5();p6();
    coend
}


經典的程序同步機制

生產者-消費者問題
  • 描述:這是一個生產者生產訊息後,消費者消費的合作關係
  • 消費者消費的空白緩衝塊由生產者生產
  • 程序在佇列操作上是互斥的。
  • 緩衝池是有大小限制的,也就是最多就生產X個
  • 確定訊號量
  • full:表示緩衝池中滿緩衝塊數量
  • empty:表示緩衝池中空緩衝塊數量
  • mutex,表示對緩衝池的互斥訪問
  • 因此可以確定,緩衝區數量必然等於full+empty,mutex=1來執行互斥訪問。
int in=0,out=0;
item buffer[n];
semaphore mutex=1,empty=n,full=0;
	
// 生產者
void producer(){
	do{
		produce 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+1) % n;  
        signal(mutex);  // 
        signal(empty);   // 增加空白塊
        consume the item in nextc;
              …
    }while(TRUE)
}

void main(){
	cobegin
		producer();
		consumer();
	coend;


}


  • P操作的順序很重要,不然會產生死鎖
利用AND訊號量解決生產者-消費者問題
int in=0,out=0;
item buffer[n];
semaphore mutex=,empty=n,full=0;
void producer(){
	do{
		producer an item nextp;
		Swait(empty, mutex);
		buttfer[in]=nextp;
		in = (in+1)%n;
		Ssignal(mutex,full);
		}while(1);	//	這裡其實就是把雙重申請換成了單獨S申請,其他的是一樣的
}
// 下略

哲學家進餐問題
  • 五個哲學家,五根兒筷子

  • 解決衝突的第一種方案,現制定順序,比如先拿左邊的筷子,

  • 如果出現都拿了自己左手邊的,等待右手邊的筷子空閒下來,那就死鎖了,所以要想辦法改變一下,變成四個哲學家拿左邊的,這樣就能保證至少一個人能吃上飯,然後釋放。

  • 但是這玩意程式碼咋寫…

  • 程式碼略

reader & writer 問題
  • 允許同時讀
  • 不允許同時寫
  • 不允許有讀有寫
  • 設計:讀rmutex,寫wmutex
  • 讀的時候加上寫鎖,寫的時候加上寫鎖和讀鎖

// reader開始讀
void reader(){
	do{
		wait(rmutex);  // 讀的時候,上讀鎖
		if(readcount==0)
			wait(wmutex);   // 第一個讀者開始的時候不允許其他人寫
		readcount++;
		signal(remutex);		// 釋放臨界區(readcount)
		.....
		// 寫完了,需要destroy了
		wait(rmutex);    // 訪問讀者的臨界區
		readcount --;
		if(readcount==0)
			signal(wmutex);	//	最後一個讀者都沒有了這個時候允許寫
		signal(rmutex);
	}while(1);

}

void writer(){
	do{
		wait(wmutex);		//	 寫臨界區鎖開始開啟
		........
		write...
		.......
		signal(wmutex);		// 臨界區鎖關閉
	}while(1);

}

  • 感悟
  • 基本上mutex是用來表示臨界區的開啟關閉的,wait開啟臨界區,上臨界區鎖,signal關閉臨界區,上臨界區鎖…
  • 記錄資料的,基本上可以算作是臨界區裡的,也就是這裡的readcount ++ - - 對它的操作必須是互斥的。