Peterson演算法的簡單分析
最近作業系統學到程序同步,介紹了Peterson演算法。
第一種(謙讓式)
上課教材《作業系統概念》第七版(翻譯版)和百度百科,還有查到的絕大部分的資料中,都是下面這種方法實現的。
下面程式碼是教材中摘的(P169)。
/*程序Pi的結構*/
do{
flag[i] = TRUE;
turn = j;
while(flag[j] && turn == j);
//臨界區
flag[i] = FALSE;
//剩餘區
} while(TRUE);
為什麼說這種演算法是一種“謙讓”的演算法呢?因為當程序i將flag[i]設定為TRUE之後,又把turn設定為j。
flag[i]=TRUE表示:程序i想進入臨界區。
而turn = j表示:現在輪到程序j進入臨界區了。
所以程序i的進入區程式碼是這樣的
flag[i] = TRUE;
turn = j;
while(flag[j] == TRUE && turn == j);
程序j的進入區程式碼是這樣的:
flag[j] = TRUE;
turn = i;
while(flag[i] == TRUE && turn == i);
分析:
首先,如果是程序i第一次開始執行,那它可以順利進入臨界區,因為flag[j]=FALSE,程序j還不想進入臨界區!
其次,如果程序i和程序j已經在併發執行了,它們的排程順序是未知的,假設每個程序每次執行一行程式碼,交替執行。那先執行的程序就“賺了”,比如程序i先執行,那麼它會先將turn“謙讓”地設定為j,但接下來輪到程序j執行了,它也“謙讓”地將turn設定為i。這時又輪到了程序i執行了,而且我們可以發現,while中第二個條件已經不滿足了!這時程序i就進入了臨界區!然後我們把情況一般化,不再假設每個程序交替地執行一行程式碼,只要一個程序後執行了turn = i;(或turn = j;)這條語句,那麼另一個程序就可以進入臨界區。(分析的時候重點關注一點:另一個程序到底想不想進入臨界區?)
第二種
只在《現代作業系統》第三版中找到了這種方法。下面程式碼是書上摘的(P69)。
沒找到第四版的書和資源,有可能第四版也改成第一種實現方法了。所以我很奇怪是不是利用flag和turn實現的加鎖都叫Peterson演算法?
#define FALSE 0 #define TRUE 1 #define N 2 /* 程序數量 */ int turn; /* 現在輪到誰?*/ int interested[N]; /* 所有值初始化為0(FALSE)*/ void enter_region(int process) /* 程序是0或1 */ { int other; /* 其他程序號 */ other = 1 - process; /* 另一方程序 */ interested[process] = TRUE; /* 表名所感興趣的 */ turn = process; /* 設定標誌 */ while(turn == process && interested[other] ==TRUE); /* 空語句 */ } void leave_region(int process) /* 程序:誰離開?*/ { interested[process] = FALSE; /* 表示離開臨界區 */ }
分析:
這種演算法本質上和第一種的區別就在於進入區
(變數命名不同
process表示當前程序自身,other表示另一個程序
interested表示flag)
interested[process] = TRUE;
turn = process;
while(turn == process && interested[other] == TRUE);
可以看到這種方法不再謙讓了,程序自己想進入臨界區,於是把turn設定成自己。
所以兩個程序都把turn設定為自己的程序號,但只有後被儲存進去的程序號才有效,前一個寫進去的因為被重寫而丟失。
分析:
首先,還是假設程序i第一次開始執行,那它可以順利進入臨界區,因為interested[j]=FALSE,程序j還不想進入臨界區!
其次,交替執行的過程中,假設某一時刻程序i正處於臨界區,那麼interested[i] = TRUE 且 turn = i,那麼這時如果程序j也想進臨界區,它會先把interested[j]改為TRUE,然後把turn改為j。但它會在while迴圈那裡忙等待,直到程序i退出了臨界區,把interested[i]改為FALSE。
總結
無論是否“謙讓”,都實現了程序互斥,並滿足了這三點:
- 同一時刻只有一個程序能進入臨界區
- 前進:不在臨界區的程序不能阻止另一個程序進入臨界區
- 有限等待:不得使程序無限期等待進入臨界區