實驗:經典同步問題之讀者寫者問題
關於前面討論過的共享記憶體問題,為了讓它能夠儲存buffersize個元素,我們修改其**,如下。
生產者:
while (true)
消費者:
while (true)
乍一看,消費者和生產者**都正確,但是當這兩段**併發執行的時候就有問題了。併發時,兩個程序會同時操作counter.這就會導致競爭條件。從而出現錯誤。為了避免這樣的錯誤,同一時間我們只能允許乙個程序操作counter。
臨界區就是乙個**段,在這部分**段中,會改變共同變數、共同表等東西。所以我們需要有這樣乙個機制,用來保證乙個程序進入臨界區,沒有其他程序被允許在臨界區內執行。
每個程序必須請求進入其臨界區,實現這一請求的**段稱為進入區,臨界區之後可有退出區。其他**·段稱為剩餘區。
臨界區問題的解答必須滿足以下三個要求:
有兩種辦法解決作業系統內的臨界區問題:搶占核心與非搶占核心。
非搶占核心比較簡單,因為同一時刻只有乙個程序在核心模式,不會發生競爭條件。而搶占核心就不同了。
boolean testandset(boolean *target)
使用testandset的互斥實現:將lock初始化為false.
dowhile(true);
void swap(boolean *a, boolean* b)
實現互斥的方法如下:其中key為區域性變數:,lock為全域性變數,初始化為false。
dowhile(true);
boolean waiting[n];
boolean lock;
演算法如下:
do while (true);
解析:
上述演算法在執行過程中,當waiting[i]和key有乙個為假時才會進入臨界區,也就是說,第乙個到達的程序進去以後lock馬上變為真,那麼其他程序必須等待,當已經進去的程序完成任務以後,他會去查詢乙個正在等待進入臨界區的程序,如果此時只有乙個程序,那麼將lock釋放,允許進入。如果找到了乙個正在等待進入臨界區的程序,那麼將它的waiting置為false,它就可以進去了。
wait()
和signal()
其定義如下:
wait(s)
signal(s)
上述兩條語句都是原子語句。每句必須不可分的執行。
互斥鎖的實現:
mutex初始化為1。
dowhile(true);
這裡定義的訊號量主要的缺點是忙等待
。就是說當乙個程序位於其臨界區內時,其他程序必須在其進入**中連續迴圈,來等待訊號量。忙等待浪費了cpu時鐘。
這種訊號量也稱為自旋鎖。自旋鎖有其優點,不需要進行上下文切換,由於上下文切換會花費大量時間,所以在等待時間較短時,自旋鎖節省了大量上下文切換的時間。自旋鎖在多處理器系統中比較常用,乙個程序阻塞,其他程序可在臨界區內執行。
為了克服忙等待,設定乙個鍊錶,當乙個程序在迴圈等待時,將其加入等待佇列。
具體實現如下:
定義乙個結構體:
typedef struct samephore;
wait(samephore * s)
}signal(samephore *s)
}
block()是掛起呼叫它的操作。
wakeup(p)是重新喚起阻塞程序p的執行。
這種實現中訊號量的值可以為負,此時它的絕對值表示等待訊號量的程序的個數。
裡面有實驗報告和原始碼,直接戳這裡
作業系統概念(第七版) 第六章 程序同步
目錄 臨界區問題 peterson演算法 硬體同步 訊號量 semaphore 計數訊號量 和 二進位制訊號量 173p 忙等待 和 自旋鎖 174p 死鎖與飢餓 有限快取問題 生產者消費問題 177p 讀者 寫者問題 管程 協作程序是可以與系統中其它執行的程序之間互相影響的程序。如果乙個程序是協作...
作業系統原理第六章(程序排程)
一 排程 分派結構 排程 依照完全確定的策略將一批程序進行排序 分派 從就緒佇列中移出乙個程序並給它提供處理機的使用權 排程程式負責將乙個程序插入到就緒佇列中,並按一定原則保持佇列結構 分派程式將程序下從就緒佇列中移出並建立該程序執行的機器狀態。二 程序排程的功能和排程準則 1.程序排程的功能 記錄...
作業系統 第六章 同步
協作程序能與系統內的其他執行程序相互影響。協作程序或能直接共享邏輯位址空間 即 和資料 或能通過檔案或訊息來共享資料,然而共享資料的併發訪問可能導致資料的不一致 共享資料併發 並行訪問 例 有界緩衝 防止競爭條件方法 併發程序同步或互斥 互斥若能保證諸程序互斥進入關聯的臨界區,可實現對臨界資源的互斥...