通過硬體的支援實現互斥
一、中斷禁用
單處理機:在單處理機中,併發程序不能重疊,只能交替,作業系統通過中斷來實現程序的交替,那麼就會存在一個問題,當一個程序A執行到臨界區時發生了中斷,另一個程序B執行,程序B也可以執行臨界區程式碼,這樣就改變了臨界資源,當程序A再接著中斷的位置執行時相關資料有可能已經被程序B修改,出現錯誤。
這個問題可以通過禁用中斷來解決,禁用中斷和啟用中斷都是原語操作。通過這個方法來實現互斥如下:
while(true)
{
/* 禁用中斷 */
/* 臨界區 */
/* 啟用中斷 */
/* 其餘部分 */
}
中斷禁用的優缺點:
優點:臨界區不可被中斷,故此可以保證互斥
缺點:由於處理器被限制於只能交替執行程式,因此執行的效率有明顯的降低
二、專用機器指令
多處理機:在多處理機配置中,幾個處理器共享記憶體,處理器間的行為是對等的,處理器之間沒有支援互斥的中斷機制。在硬體級別上,對儲存單元的訪問排斥對相同單元的其他訪問。基於這一點,處理器的設計者提出了一些機器指令,用於保證兩個動作的原子性,即某一時刻只能有一個臨界區程式碼訪問臨界資源。兩種最常見的指令是:比較和交換指令、交換指令。
- 比較和交換指令如下:
int compare_and_swap(int *word, int testval, int newval) { int oldval; oldval = *word; if(oldval == testval) *word = newval; return oldval; }
示例:
const int n = /* 程序個數 */
int bolt;
void p(int i)
{
while(true)
{
while(compare_and_swap(bolt, 0, 1) == 1)
{
/* 不做任何事 */
}
/* 臨界區 */
bolt = 0;
/* 其餘部分 */
}
}
void main()
{
bolt = 0;
parbegin (p(1), p(2), ... ,p(n));
}
程序開始,bolt = 0當某一個程序A率先執行比較和交換指令時可以通過,通過之後便將bolt改為1,而測試值是0,因此其他程序執行比較和交換指令時不能通過,處於忙等待狀態(在程序得到臨界區訪問權之前,它只能繼續執行測試變數的指令來得到訪問權,除此之外不能做其他事情),當程序A執行完臨界區之後又將bolt值改為0, 則將會有一個程序測試通過,往復執行,即可保證某一時刻只有一個臨界區程式碼訪問臨界資源。
- 交換指令如下:
void exchange(int *register, int *memory)
{
int temp;
temp = *memory;
*memory = *register;
*register = temp;
}
示例:
int const n = /* 程序個數 */;
int bolt;
void p(int i)
{
int keyi = 1;
while(true)
{
do exchange(&keyi, &bolt)
while(keyi != 0)
{
/* 臨界區 */
}
bolt = 0;
/* 其他部分 */
}
}
void main()
{
bolt = 0;
parbegin (p(1), p(2), ... ,p(n));
}
程序開始,bolt = 0當某一個程序A率先執行交換指令時可以通過,通過之後便將bolt改為1,因此其他程序執行交換指令時bolt = 1,通過交換將keyi置為1,則將一直處於while迴圈中,無法執行下邊臨界區。當程序A執行完臨界區之後又將bolt值改為0, 則將會有一個程序測試通過,往復執行,即可保證某一時刻只有一個臨界區程式碼訪問臨界資源。
機器指令方法的優缺點:
優點
- 適用於在單處理器或共享記憶體的多處理器上的任何數目的程序
- 非常簡單且易於證明
- 可用於支援多個臨界區,每個臨界區可以用它自己的變數定義
缺點
- 使用了忙等待:因此當一個程序正在等待進入臨界區時,它會繼續消耗處理器時間
- 可能飢餓:當一個程序離開一個臨界區並且有多個程序正在等待時,選擇哪一個程序是任意的,因此某些程序可能被無限拒絕進入。
- 可能死鎖:當某個程序通過專門指令時,在臨界區中發生中斷將有可能發生死鎖。