Java併發編程之進程

引入

操作系統中最核心的概念是進程:這是對正在運行程序的一個抽象。一個進程就是一個正在執行程序的實例、包括程序計數器、寄存器和變量的當前值。在多道程序設計中,一個 CPU 能在多個進程之間來回快速切換,達到(僞)並行效果。一個進程是某種類型的一個活動,它有程序、輸入、輸出以及狀態。

操作系統的關鍵抽象:

這裏寫圖片描述

內存大小有限,操作系統會爲每個進程分配獨立的虛擬地址空間。虛擬地址空間會映射到物理地址空間。JAVA虛擬機和VMWARE並沒有CPU,硬盤等,可以說是一個軟的虛擬機。

比喻 : 廚師做蛋糕

  • 做蛋糕的食譜: 程序
  • 做蛋糕的原料:輸入數據
  • 廚師: CPU
  • 廚師閱讀食譜,用原料做蛋糕的一系列動作的總和: 進程

  • 廚師的兒子跑進了,說是被蜜蜂蟄了

    • 廚師記錄下當前做到哪一步了(保存當前進程狀態)
    • 拿出急救手冊,按其中的指示進行處理(開始另外一個進程)

進程在虛擬地址存儲器中的邏輯佈局

這裏寫圖片描述

每個進程都有一個虛擬的地址空間, 這個地址空間是分塊的。

這裏寫圖片描述

程序運行中使用了多個寄存器,但是CPU只有一套寄存器。進程切換,寄存器的值需要保存在內存中,否則會被其他進程沖掉。

內存中的進程

這裏寫圖片描述

struct task_struct{
    pid_t pid; //進程號
    long state;  //狀態
    cputime_t utime, stime;     // cpu在用戶態和核心態下
                                                        執行的時間
    struct  files_struct *files;    //打開的文件
    struct mm_struct *mm;    //進程使用的內存
    ......
}

進程的狀態

這裏寫圖片描述

運行–>>就緒: 進程在CPU中時間片用完
運行–>>等待:IO操作等。 等待就是說放入CPU維護的一個隊列中去,根本沒有佔用cpu,真正佔用CPU的是運行中的進程。

進程調度:到底誰應該佔據CPU

  • 非搶佔式
    • 調度程序一旦把 CPU分配給某一進程後便讓它一直運行下去, 直到進程完成或發生某事件而不能運行時,纔將CPU分給其它進程。
    • 適用於批處理系統
    • 簡單、系統開銷小。
  • 搶佔式
    • 當一個進程正在執行時,系統可以基於某種策略剝奪CPU給其它進程。剝奪的原則有: 優先權原則、短進程優先原則、時間片原則
    • 適用於交互式系統

進程調度:評價標準

  • 公平

    • 合理的分配CPU
  • 響應時間短

    • 響應時間: 從用戶輸入到產生反應的時間
  • 吐量大

    • 吞吐量: 單位時間完成的任務數量
  • 但是, 這些目標是矛盾的!

批處理系統中的調度

  • 先來先服務

    • 公平、簡單(FIFO隊列)、非搶佔、不適合交互式
  • 最短作業優先

    • 系統的平均等待時間最短
    • 但是需要預先知道每個任務的運行時間

交互式調度策略(1) :輪轉

  • 每個進程分配一個固定的時間片

  • 假設進程切換一次的開銷爲1ms

  • 時間片爲4ms

    • 20%的時間浪費在切換上
  • 時間片爲100ms
    • 浪費只有1%, 但是假設有50個進程, 最後一個需要等待5秒 !

交互式系統調度策略(2):優先級

這裏寫圖片描述
這裏寫圖片描述

缺點:低優先級的進程可能會被餓死(永遠得不到執行)。

調度策略(3):多級隊列反饋

這裏寫圖片描述

每個級別都有若干進程,每次執行都選擇高優先級的進程執行,高優先級執行完了在執行低優先級。每一個進程執行完放到同一隊列的尾部,同一級別採用時間片輪轉策略。優先級高分配的時間短。還是會出現低優先級餓死,任務的優先級隨着等待的時間增長優先級不斷增高,動態調整。

進程間同步

這裏寫圖片描述

生產者進程向隊列中加入文件,消費者進程從隊列中取走文件

生產消費模型代碼實現:
這裏寫圖片描述

  • 多進程情況下, 共享變量counter會出錯!

這裏寫圖片描述

  • 上圖中 regist 類比成 寄存器
  • 對一個變量的操作,編譯成機器碼會是多行代碼。所以我們在JAVA中寫的count++不是一個原子操作。亂序執行的情況下就會出問題。

反思一下

這裏寫圖片描述

  • 問題的核心

    • 不可控制的調度
    • 在機器層面, counter++, counter– 並不是原子操作
  • 臨界區

    • 訪問/修改共享資源(變量, 表,文件。。。)
    • 當進程進入臨界區時,不允許其他進程在臨界
      區執行

解決臨界區問題

解決臨界區問題(1):暴力手段,關閉中斷

  • CPU 收到時鐘中斷以後,會檢查當前進程的時間片是否用完, 用完則切換。

  • 在進程P進入臨界區之前,通知OS, 不要做進程切換不就行了!

    • 關閉中斷 (時鐘中斷), 這樣CPU就不會被打斷
    • 離開臨界區,一定要記住打開中斷
    • 但是,把中斷操作開放給應用程序是非常危險的。會讓宕機。

解決臨界區問題(2):用硬件指令來實現鎖

這裏寫圖片描述
- 上圖三條代碼是原子的
這裏寫圖片描述

爲了確保原子性,系統會鎖住總線,禁止其他CPU在函數執行完之前去訪問內存

解決臨界區問題(3):信號量

信號量S是個整數變量,除了初始化外,有兩個操作,wait(), signal()
或者是P/V , 或者 down/up。

僞代碼:
這裏寫圖片描述
爲了確保信號量能工作,需要用一種“原子”的方式實現它

解決臨界區問題(5):不能忙等

這裏寫圖片描述

list維護了一個等待進程列表。不像上一種方式在鎖住的情況下,進程雖然獲得了時間片但是什麼事也幹不了。通過維護一個等待進程列表解決了忙等問題。

解決臨界區問題(6):用信號量解決打印問題

這裏寫圖片描述

思考

  • 兩個進程之間是怎麼共享變量的?
    兩個進程之間通過操作系統內核共享數據
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章