Home >> Blog >> 什麼是 semaphore?

跳開SEO搜尋引擎優化的軟體框架,今天來聊聊硬體內部機制semaphore。

什麼是 semaphore?

在本教學中,我們將深入研究一個功能強大且廣為人知的進程同步工具:semaphore。

我們將研究semaphore操作、類型及其實現。然後我們將探討一些使用semaphore幫助我們克服可能的進程同步問題的multi-stream案例。

2.什麼是semaphore?

semaphore是一個整數變量,在多個進程之間共享。使用semaphore的主要目的是在並發環境中對公共資源進行進程同步和訪問控制。

semaphore的初始值取決於手頭的問題。通常,我們使用可用資源的數量作為初始值。在接下來的部分中,我們將給出更多在不同用例中初始化semaphore的範例。

它是一個不需要忙於等待的同步工具。因此,當進程由於無法訪問資源而無法運行時,操作系統不會浪費 CPU 週期。

2.1。semaphore操作

semaphore有兩個不可分割的(原子)操作,即:\textbf{\textit{等待}}和\textbf{\textit{信號}}。這些操作有時被稱為0 and 無, or 下and向上在某些情況下

在本文中,讓我們關注在操作系統內核中實現的semaphore。因此,等待and信號操作被實現為系統調用。

設小號是一個semaphore,它的整數值代表一些可用資源的數量。現在讓我們檢查一下等待操作是如何工作的:

什麼是semaphore?

如果它大於零(有可用資源分配),該等待函數會簡單地遞減。小號如果小號已經為零(沒有可分配的可用資源),則調用進程進入睡眠狀態。

現在讓我們檢查一下信號 操作

如果信號沒有其他進程在等待資源,則操作遞增 S。否則,不是增加它的值,而是選擇一個等待進程被操作系統調度程序喚醒。結果,該進程獲得了對資源的控制權。

2.2. semaphore類型

有兩種類型的semaphore:

.二進制semaphore

.計數semaphore

二進制semaphore只能有兩個整數值:0 或 1。它更易於實現並提供互斥。我們可以使用二進制semaphore來解決臨界區問題。

一些有經驗的讀者可能會將二進制semaphore與 互斥體混淆量混淆。有一個常見的誤解是它們可以互換使用。但實際上,semaphore是一種信號機制,而互斥量是一種鎖定機制。所以,我們需要知道二進制semaphore不是互斥體。

計數semaphore也是一個整數值,其範圍可以跨越一個不受限制的域。我們可以用它來解決資源分配等同步問題。

3.semaphore實現

如上所述,我們專注於在操作系統內核中實現的semaphore。

沒有忙等待的實現需要一個整數值(保存semaphore值)和一個指向等待列表中下一個進程的指針。該列表包含在操作中進入睡眠狀態的進程等待。內核使用兩個額外的操作:堵塞和醒來, 來命令進程。

我們可以將semaphore實現視為一個關鍵部分問題,因為我們不希望多個進程同時訪問semaphore變量。

幾種編程語言支持並發和semaphore。例如,Java 支持semaphore,我們可以在multi stream程序中使用它們。

4.進程同步

在multi stream環境中,進程同步意味著並發進程有效地共享系統資源。確保同步執行需要一種方法來協調使用共享資料的進程。令人滿意的同步機制在避免死鎖和飢餓的基礎上提供並發性。

小心使用,semaphore是一個強大的同步工具,可以實現進程同步。讓我們看看接下來如何處理一些問題。

4.1。避免死鎖

當一組進程在等待其他成員操作的狀態下被阻塞時,就會發生死鎖。 為了避免可能的死鎖,我們需要小心我們如何實現semaphore。我們來看一個死鎖的例子:

什麼是semaphore?

在這裡,進程\:0並進程\:1有一些應該單獨運行的程式碼。

考慮進程\:0執行 wait( 小號) 並在此之後立即中斷的情況。然後讓我們假設進程\:1開始運行。最初,進程\:1在 semaphore問上等待,然後在 wait( 小號) 語句中被阻塞,因此進入睡眠狀態。

當內核進程\:0再次運行時,排隊的下一個操作是等待(問)。由於semaphore問已經被 使用進程\:1,現在我們遇到了兩個進程都在等待對方的情況,因此出現了死鎖。

如果我們要顛倒任何進程中的行順序並使用公共排序,我們將在此範例中省略死鎖。

4.2. 避免飢餓

我們需要注意的下一個概念是飢餓。 飢餓意味著進程的無限期阻塞。例如,死鎖情況可能會導致某些進程出現飢餓。

一個著名的飢餓範例是哲學家就餐問題,我們將在以下部分中介紹。在這個問題中,五位哲學家圍坐在一張圓桌旁。他們共用五根筷子。每個哲學家要麼思考,要麼吃飯。吃飯,哲學家需要兩根筷子:

什麼是semaphore?

這個假設的問題象徵著飢餓。如果一個哲學家的進程由於無法使用筷子而永遠被阻塞,它就會餓死。

使用進程同步,我們希望確保每個進程遲早都會得到服務。

4.3. 優先級反轉

semaphore使用效率低下可能導致的另一個問題是優先級反轉。優先級反轉當較低優先級的作業優先於較高優先級的作業時,就會發生

讓我們假設一個低優先級的進程持有一個semaphore,而這個semaphore是高優先級進程所需要的。此外,假設中等優先級進程也在調度程序隊列中等待:

什麼是semaphore?

在這種情況下,內核不會調度低優先級進程阻塞高優先級進程。相反,內核繼續執行中等優先級的作業,導致高優先級的作業繼續等待。

一種可能的解決方案是在semaphore上使用優先級繼承。

5. semaphore在行動

現在我們已經定義了semaphore是什麼,讓我們來探索一些semaphore的用例。

5.1。臨界區問題

臨界區是程序程式碼的一部分,我們希望避免並發訪問。我們可以使用二進制semaphore來解決臨界區問題。在這種情況下,semaphore在內核中的初始值為 1:

什麼是semaphore?

在上面的例子中,我們保證臨界區訪問中的互斥。

等待進程不是忙著等待,而是在等待打開臨界區時處於休眠狀態。

然後進行信號操作,內核在就緒隊列中加入一個休眠進程。如果內核決定運行該進程,它將繼續進入臨界區。

通過這種方式,我們確保任何時候只有一個進程處於臨界區。

5.2. 按訂單執行

假設我們要執行S1之前的程式碼片段S2。換句話說,我們希望進程\:1在執行之前等待,S2直到進程\:0完成執行S1:

什麼是semaphore?

我們可以通過使用semaphore輕鬆克服這個問題,將其初始值設置為 0。讓我們修改程式碼:

什麼是semaphore?

通過使用 semaphore X,我們保證進程\:1在執行該部分之前等待S2直到進程\:1執行S1.

5.3. 生產者消費者問題

接下來讓我們考慮眾所周知的生產者-消費者問題。在這個問題中,有一個有限的緩衝區,由緩衝區\_size單元組成。換句話說,緩衝區最多可以存儲緩衝區\_size元素。兩個進程正在訪問緩衝區,即:製片人和消費者。

為了克服這個問題,我們使用一個計數semaphore來小號表示完整單元格的數量:

什麼是semaphore?

最初,S = 0. 當生產者將一個項目放入緩衝區時,它通過信號操作增加semaphore。相反,當消費者消費一個項目時,通過等待操作,semaphore減少。

當消費者使用緩衝區中的最後一項時,最後一個等待操作將其置於睡眠狀態。

5.4. 有界緩衝區生產者-消費者問題

在上面的解決方案之上,如果緩衝區已滿,我們可以通過再添加兩個semaphore來強制生產者休眠。假設我們引入了空的表示緩衝區中可用單元數的semaphore。我們還介紹了 semaphore 滿的,表示緩衝區中完整單元的數量。在這種情況下,我們小號在緩衝區上使用semaphore。同樣,生產者和消費者進程同時運行。

我們修改生產者和消費者程式碼以包含semaphore:

什麼是semaphore?

這裡,計數semaphore滿的表示完整單元格的數量,而計數semaphore空的表示有界緩衝區上可用的單元格數量。此外,semaphore S 保護緩衝區免受並發訪問。

我們先觀察生產者的行為:

如果空的semaphore變為 0,則生產者進入睡眠狀態,因為緩衝區中沒有更多的空單元格。

此外,生產者小號在訪問緩衝區之前等待semaphore。如果另一個進程已經在等待小號,它將進入睡眠狀態。

在產生一個新項目後,生產者發出信號滿的量,從而觸發喚醒任何可能的消費者任何等待進程。

同樣,消費者使用相同的semaphore:

如果滿的semaphore為零,則緩衝區中沒有等待的項目。消費者進入睡眠狀態。

等待操作小號確保在給定時間只有一個進程訪問緩衝區。因此,信號 on小號喚醒任何等待的進程。

最後,semaphore上的空的信號喚醒任何有東西要放入緩衝區的生產者。最後,消費者準備好消費下一個項目。

此解決方案確保生產者休眠,直到緩衝區中有可用的插槽。相反,消費者會一直睡覺,直到有物品可以消費。

5.5. 讀者和作家問題

這個問題有幾個讀者和作家,在同一個資料集上工作。讀者進程只讀取資料集;他們不執行任何更新。而作家進程既可以讀取也可以寫入。

我們這裡的同步問題是在任何給定時間只允許一個寫入器訪問資料集。我們可以允許多個讀者同時閱讀,沒有任何問題。

假設我們鎖定了資料集。第一個讀者應該鎖定資料集,然後讀者可以繼續並開始讀取。最後一個讀者應該在完成讀取時釋放鎖。

當 a作家開始閱讀或停止閱讀時,它應該鎖定/解鎖其他作家和的資料集讀者。

為了在這個問題中啟用進程同步,我們可以使用一個整數值和兩個semaphore。

讓我們將整數初始化讀_\計數為零,它表示讀者訪問文件的次數。這不是semaphore,只是一個整數。

另外,讓我們將semaphore初始化小號為 1,這樣可以保護讀_\計數變量不被多個進程更新。

最後,假設我們將semaphore初始化寫為 1,這樣可以保護資料集不被多個作家. 作家所以和過程的偽程式碼讀者:

什麼是semaphore?

在這種情況下,作家只有在 semaphore 上等待和發出信號寫。如果它可以獲取寫semaphore,它可以繼續。否則,等待操作將其阻塞。

一旦寫入操作完成,寫入器就會發出semaphore信號寫。因此,其他進程可以訪問資料集.

A讀者的工作流程更複雜。它做的第一件事是增加讀\_count. 為此,讀者需要獲取semaphore小號來修改讀\_count值,以確保它不會被多個進程同時訪問。

如果此時沒有其他人讀者正在訪問資料集,則讀\_count在增量操作後變為 1。在這種情況下,在訪問資料集之前讀者等待semaphore寫,以確保沒有寫入器正在訪問。

釋放semaphore後小號,讀者繼續執行讀取操作。

最後,當讀者完成對資料集的處理時,需要遞減該讀\_count值。再次,它獲取 semaphore 小號,更改讀\_count變量,最後釋放 semaphore 小號。

5.6. 餐飲哲學家問題

Dining Philosophers 問題是一個眾所周知的用於測試同步工具的思想實驗。

我們有五位哲學家圍坐在一張桌子旁,手裡拿著五根筷子。哲學家想吃東西,就需要準備兩根筷子。在這個問題中,筷子是我們要在進程之間同步的資源。

讓我們使用semaphore來實現一個解決方案。首先,我們初始化semaphore,將五根筷子表示為一個5的數組。然後,我們實現a餐飲\:哲學家為:

什麼是semaphore?

在這個解決方案中,當哲學家一世決定吃飯時,它首先等待 semaphore 一世。然後在獲得它之後,哲學家等待 semaphore (i+1)\%5。當兩根筷子都獲得時,它就可以吃東西了。

當哲學家吃飽時,它通過調用相應semaphore上的信號操作,以相同的順序釋放筷子.

們需要記住,這個例子並不能保證一個無死鎖的解決方案。我們可以實施一個小的調整來保證所有三個條件。