上一篇計算機系統009 - 操做系統之進程中講述了操做系統設立進程、線程的概念主要是爲了便於切換管理,同時也提到,在單處理器結構系統中,進行進程或線程級別的切換能夠在承擔必定切換消耗後下降平均響應時長。而隨着硬件技術的發展,主要是多處理器架構的興起,操做系統中並行的概念也逐漸成爲主流。 算法
既然提到多處理器架構的興起,也就再多說幾句。最開始那段時間,也就是電氣CPU的年代,CPU既重又笨。第一次飛躍是隨着晶體管硬件技術的革新而來,主頻獲得了極大提高,隨後的第二次飛躍則是由集成電路所推進。發展到了這裏,CPU製造商卻發現經過提高主頻、緩存所能換來的提高效益與成本相比,性價比很低。天然地,有一波人就打起了量的主意。經過將多個CPU核心,甚至多個CPU自己集成入芯片,同時處理多個可分割的計算任務,達到下降平均執行時長的目的。簡單的來講,就是經過增長更多的服務窗口,使得排隊的時間減小,進而減小總體執行時間。 緩存
介紹其餘內容以前,先對並行、併發兩個概念稍加說明。理解這兩個概念時能夠類比程序和進程的概念,具體以下: 網絡
併發Concurrency)
Concurrency是指程序、算法或問題自己可分解爲順序無關或不徹底依賴執行順序單元的屬性,從語法來說,併發雖然是一個名詞,但能夠當作是表達爲擁有某種能力的形容詞。也就是說假如一個程序具備併發的屬性,那麼即便將分解後單元以亂序或局部順序執行,最終結果也不會受影響,這爲單元並列執行提供了基礎。
簡而言之,併發是隻程序在結構上擁有一種能力,與執行自己無關。 多線程
並行Parallelism
如前所述,並行的全稱能夠視爲並列執行,Parallelism指的是多個單元或多個任務正在同時執行,強調的是一種現象。若是說併發是靜態的程序能力,那麼並行就是這種能力動態發揮做用的現象自己。 架構
插一句題外話,因爲目前使用的大部分計算機系統概念都造成於西方,所使用的語言也以英文爲主,漢化翻譯的過程當中,有時候很難找到十分貼切的詞彙去映射抽象的概念本體,致使直面概念的中文詞彙(如進程、線程、纖程、並行、併發)時很難獲取到精確理解。這時候,仍是建議先找到概念的英文表示,從英文自己的解釋出發,融匯理解。 併發
回到並行自己,並行的最大特徵是同時執行,那麼對應的硬件前提就是切實存在多個CPU核心甚至多個CPU,以此支持線程、進程的調度。前面的章節中咱們講到,進程、線程自己是須要使用地址空間、棧空間、以及其餘相關硬件資源的。按照單處理器架構執行時,始終能保證同一時刻,只會有一個進程或線程運行,因此在資源共享這一塊不會出現太多問題。但在多處理器多核架構下多個進程、線程同時執行,隨時有可能發生兩個進程、線程須要使用同一資源(包括CPU時間、存儲器、文件以及I/O設備等)的現象。 post
舉個例子,大部分人應該都有包餃子的經歷,假如如今有一碗餡料,裏面只有一根勺子,那麼當只有一我的在包餃子時,他能夠隨時獲取到勺子的使用權,並在不用時將其閒置等待;假如如今仍是一根勺子,但有兩我的在包餃子,因爲你們包餃子的節奏確定不會絕對相同,那麼在獲取勺子使用權時就存在以下幾種狀況: 測試
前面兩種還好,既不會太閒置,也能和諧進行。但第三種狀況就尷尬了,到底誰該縮回伸出去的手?雖然無比尷尬,但既然發生了問題,終歸仍是要解決的。從包餃子的所遇到的問題中,能夠發現並行的兩大核心問題是同步和通訊,同步時爲了互斥,通訊是爲了合做。 操作系統
同步,英文表示爲Synchronization,生活中的解釋是協調事件共同操做某一系統,而在計算機領域,這個同步指代兩個方面的同步: 線程
進程同步
多個進程在某一特定位置進行鏈接或握手,以確保達成共識或基於指定次序執行。
數據同步
數據同步是指保持同一數據集多個備份的一致性,或是保證數據完整性。
上述說明的關鍵點是確保事件的發生基於指定次序,萬事總得講究個先來後到,哪怕我比你早一毫秒碰到了勺子,那也是先來,理應優先響應,得到使用權。你要想得到勺子的使用權,能夠先跟我確認,看我狀態,看我心情給不給你用。若是我不給,那你就乖乖等着,等我用完了再用。
因此伴隨着同步的是潛在互斥的結果,任什麼時候刻,只容許一我的使用某一特定資源,好比內存中某一字節,又好比打印設備等。一般對於互斥的硬件層的支持有兩種:
中斷禁用
一旦得到使用權後當即禁止中斷,直到使用結束後從新容許中斷。既然本進程沒法被中斷,天然就不存在資源衝突的問題。中斷禁用的問題是隻適用於單處理器結構的系統,多處理器結構中,每一個CPU有本身的中斷控制,而進程中只能禁止所分配到的CPU的中斷,沒法真正防止其餘CPU中進程獲取同一資源而引起衝突。
專用機器指令
將兩個動做在一個指令週期內完成來保證操做總體的原子性,實現互斥。如在一個指令週期內完成讀-寫或讀-測試兩條指令,指令執行期間會阻塞其餘指令的訪問,以達成資源級別的互斥。
前面提到,假如我正在使用勺子,而你但願使用,那就必須等待我使用結束,或是跟我兩目對視,會心一笑讓中斷下來讓你先用。操做系統中,進程或線程也須要經過通訊來確認資源當前使用狀況,有效的信息可以幫助進程及時獲取資源使用權。
一般進程(線程)之間會以以下三種狀態共存:
雙方都不知道對方存在
雙方都不知道對方的存在,那就只有操做系統掌握了雙方的關係。例如兩個進程都同時但願使用打印設備,假設該打印設備任一時刻只能服務於單個進程,那麼操做系統就必須保證二者互斥,不去同時操做打印設備。固然,如今的打印設備會在打印執行單元前將打印任務序列化,因此同時提交也能支持,只不過內部依然是依次執行單個打印任務的。
雙方間接知道對方的存在
經過共享對象來達成通訊,如生產者、消費者,除了生產、消費兩個特性外,他們均不知道各自更多的信息。
雙方直接知道對方的存在
直接知道意味着知道對方進程的基本信息,如進程ID。有了進程ID,就能夠創建二者通訊的專用線路。相似而言,假如你們都在某一共享空間如留言板上匿名留言,爲了繼續通訊,也只能在留言板上回復。而若是我知道對方真正ID,那麼就能夠直接私信進行交流了。
基於以上三種共存狀態,進程間爲了完成合做,也須要使用不一樣的通訊機制。
對於通訊機制的理解,應迴歸到適用的場景自己。
在通訊雙方都不知道對方存在的條件下,要想完成通訊,那麼必須考慮經過操做系統完成同步,這一部分直接由操做系統全局統籌實現。而在雙方間接知道對方的狀況下,最典型的方法就是信號量。信號量的基本原理是:兩個或多個進程能夠經過簡單的信號進行合做,一個進程能夠被迫在某一位置中止,直到它接收到一個特定的信號。經過調整信號結構,理論上任何複雜的合做需求均可以獲得知足。
信號量實質上可視爲一個具備整數值的變量,該變量支持三個操做:
其邏輯能夠換位到銀行窗口服務與客戶的關係,服務窗口數量就是信號量。
客戶來到銀行大廳,使用wait()申請一個窗口服務,申請後N值減1
信號量初始值表明着wait()被阻塞前所支持的進程/線程最大數目,當該值可爲任意整數時,稱爲計數信號量。而若是信號量初始值只能爲1,也就是說信號量值只能在0/1間變化,那麼該信號量也稱爲二進制信號量。一般的實現稱爲Mutex,即互斥量。和普遍分佈在整個程序中的計數信號量相比,Mutex一般只用在同一線程中,即signal()/wait()均應由同一線程調用。
管程提供和信號量類似的功能,並且在使用上更加易於控制。管程由一至多個程序、一個初始化序列以及局部數據組合而成。其主要特徵是:
前兩個特徵與面向對象封裝相似,最後一個特徵則是互斥的體現。簡而言之,管程就是經過分配有限的訪問渠道給進程,並保證任一時刻只能由一個渠道訪問數據,達到保護數據的目的。再通俗一點,管程就像是一個有多扇門的房間,房間裏面儲存了一些物品,任一時刻只要有人進入房間,就將全部門反鎖。完成對物品的操做後,再取消反鎖,退出房間。
和現實生活中同樣,消息傳遞的目的是爲了將某些訊息告知對方,便於對方及時作出反應。進程間傳遞消息也是爲了告知對方發生了什麼事件、切換到什麼狀態等變化。消息要想被雙方理解,就要遵循相同的格式。通用的消息格式以下圖所示:
除去消息類型,最主要的字段應該是源ID和目標ID,分別表明了消息的發送者和接收者。對於同一主機而言,這能夠是相互區別的進程ID便可,對於網絡中的不一樣主機而言,該字段則需添加上網絡中進行惟一標示的IP地址。
要想進一步理解各類通訊機制,仍是要回歸到問題自己。並行過程當中之因此會產生問題,關鍵在於對同一資源的競爭,若是不能協調各進程/線程有序訪問該資源,就會使得依賴於資源值的進程由於值錯誤而致使運行錯誤。
生產-消費者問題也稱爲有限緩衝問題,是一個經典的多線程同步問題。該問題中有以下三個主體:
問題關鍵在於確保生產者不會在緩衝區滿時寫入數據,消費者也不會在緩衝區中爲空時消耗數據。換句話說,緩衝區中可用部分的多少是關鍵數據。
讀者-寫者問題發生在多個線程讀或寫同一共享資源時,一般,任一時刻只容許有一個線程寫資源,且若有線程正在寫資源,則全部讀操做也會被阻塞。如無線程正在寫,則多個線程可同時讀取該資源。
通用的解決方案是使用讀寫鎖,該鎖中可將讀與寫操做互斥,而在讀自己上使用信號量進行計數便可。
本篇概念較多,但願經過屢次對比例證可以對問題進行降解。文中說起並行由來、並行過程當中潛在典型問題以及相應的解決辦法,但到目前爲止,仍止步於可能因爲對單一資源競爭引起衝突。事實上,再往壞處想,假如涉及到的資源也從單一變成了多個(如哲學家就餐問題),那麼多個進程/線程與多個資源間的相互競爭則會引起更大的災難。而這,也就是不得不單獨開篇去闡述「死鎖」的原因。