?? 盡人事,聽天命,博主東南大學碩士在讀,熱愛健身和籃球,樂于分享技術相關的所見所得,關注公眾號 @ 飛天小牛肉,第一時間獲取文章更新,成長的路上我們一起進步
?? 本文已收錄于 CS-Wiki(Gitee 官方推薦專案,現已 1.0k+ star),致力打造完善的后端知識體系,在技術的路上少走彎路,歡迎各位小伙伴前來交流學習
全文脈絡思維導圖如下:

1. 行程與執行緒的簡單解釋
行程(Process)和執行緒(Thread)是作業系統的基本概念,但是它們比較抽象,不容易掌握,以下這個解釋出自阮一峰老師的博客(http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html),雖然不是非常嚴謹,但是足夠形象,看完之后能對行程和執行緒有個非常直觀的印象,這樣也方便理解后文,
① 計算機的核心是 CPU,它承擔了所有的計算任務,它就像一座工廠,時刻在運行,

② 假定工廠的電力有限,一次只能供給一個車間使用,也就是說,一個車間開工的時候,其他車間都必須停工,背后的含義就是,單個 CPU 一次只能運行一個任務,
③ 行程就好比工廠的車間,它代表 CPU 所能處理的單個任務,任一時刻,CPU 總是運行一個行程,其他行程處于非運行狀態,

④ 一個車間里,可以有很多工人,他們協同完成一個任務,

⑤ 執行緒就好比車間里的工人,一個行程可以包括多個執行緒,

⑥ 車間的空間是工人們共享的,比如許多房間是每個工人都可以進出的,這象征一個行程的記憶體空間是共享的,每個執行緒都可以使用這些共享記憶體,

⑦ 可是,每間房間的大小不同,有些房間最多只能容納一個人,比如廁所,里面有人的時候,其他人就不能進去了,這代表一個執行緒使用某些共享記憶體時,其他執行緒必須等它結束,才能使用這一塊記憶體,

⑧ 一個防止他人進入的簡單方法,就是門口加一把鎖,先到的人鎖上門,后到的人看到上鎖,就在門口排隊,等鎖打開再進去,這就叫"互斥鎖"(Mutual exclusion,縮寫 Mutex),防止多個執行緒同時讀寫某一塊記憶體區域,
⑨ 還有些房間,可以同時容納 n 個人,比如廚房,也就是說,如果人數大于 n,多出來的人只能在外面等著,這好比某些記憶體區域,只能供給固定數目的執行緒使用,
⑩ 這時的解決方法,就是在門口掛 n 把鑰匙,進去的人就取一把鑰匙,出來時再把鑰匙掛回原處,后到的人發現鑰匙架空了,就知道必須在門口排隊等著了,這種做法叫做 "信號量"(Semaphore),用來保證多個執行緒不會互相沖突,

不難看出,互斥鎖 Mutex 是信號量 semaphore 的一種特殊情況(n = 1時),也就是說,完全可以用后者替代前者,但是,因為 Mutex 較為簡單,且效率高,所以在必須保證資源獨占的情況下,還是采用這種設計,
2. 行程基礎掃盲
① 什么是行程
結合上文的簡單解釋,下面給出行程的科學定義:行程是程式在某個資料集合上的一次運行活動,也是作業系統進行資源分配和保護的基本單位,
通俗來說,行程就是程式的一次執行程序,程式是靜態的,它作為系統中的一種資源是永遠存在的,而行程是動態的,它是動態的產生,變化和消亡的,擁有其自己的生命周期,
舉個例子:同時掛三個 QQ 號,它們就對應三個 QQ 行程,退出一個就會殺死一個對應的行程,但是,就算你把這三個 QQ 全都退出了,QQ 這個程式死亡了嗎?顯然沒有,
行程不僅包含正在運行的程式物體,并且包括這個運行的程式中占據的所有系統資源,比如說 CPU、記憶體、網路資源等,很多小伙伴在回答行程的概念的時候,往往只會說它是一個運行的物體,而會忽略掉行程所占據的資源,比如說,同樣一個程式,同一時刻被兩次運行了,那么他們就是兩個獨立的行程,
② 行程的組成
行程主要由三個部分組成:
1)行程控制塊 PCB,包含如下幾個部分:
-
行程描述資訊
-
行程控制和管理資訊
-
資源分配清單
-
CPU 相關資訊
2)資料段,即行程運行程序中各種資料(比如程式中定義的變數)
3)程式段,就是程式的代碼(指令序列)
舉個例子:同時掛三個 QQ 號,會對應三個 QQ 行程,它們的 PCB、資料段各不相同,但程式段的內容都是相同的 (都是運行著相同的 QQ 程式)

PCB 是提供給作業系統用的,而程式段、資料段是給行程自己用的,
行程控制塊 PCB
每個行程有且僅有一個行程控制塊(Process Control Block,PCB),或稱行程描述符,它是行程存在的唯一標識,是作業系統用來記錄和刻畫行程狀態及環境資訊的資料結構,也是作業系統掌握行程的唯一資料結構和管理行程的主要依據,所以說 PCB 是提供給作業系統使用的,
通俗的解釋:作業系統需要對各個行程進行管理,但凡管理時所需要的資訊,都會被放在 PCB 中,PCB 是行程存在的唯一標志,創建行程和撤銷行程等都是指對 PCB 的操作,當行程被創建時,作業系統為其創建 PCB,當行程結束時,會回收其 PCB,
一般來說,PCB 會包含如下四類資訊:
1)行程描述資訊:用來讓作業系統區分各個行程
-
當行程被創建時,作業系統會為該行程分配一個唯一的、不重復的 “身份證號”— PID(ProcessID,行程 ID)
-
另外,行程描述資訊還包含行程所屬的用戶 ID(UID)
2)行程控制和管理資訊:記錄行程的運行情況,比如 CPU 的使用時間、磁盤使用情況、網路流量使用情況等,
3)資源分配清單:記錄給行程分配了哪些資源,比如分配了多少記憶體、正在使用哪些 I/O 設備、正在使用哪些檔案等,
4)CPU 相關資訊:行程在讓出 CPU 時,必須保存該行程在 CPU 中的各種資訊,比如各種暫存器的值,用于實作行程切換,確保這個行程再次運行的時候恢復 CPU 現場,從斷點處繼續執行,這就是所謂的保存現場資訊,

③ 行程的狀態
盡管每一個行程都是獨立的物體,有其自己的 PCB 和內部狀態,但是行程之間經常需要相互作用,一個行程的輸出結果可能是另一個行程的輸入,假設行程 A 的輸入依賴行程 B 的輸出,那么在行程 B 的輸出結果沒有出來之前,行程 A 就無法執行,它就會被阻塞,這就是行程的阻塞態,
經典的行程三態模型如下:
-
運行態(running):行程占有 CPU 正在運行,
-
就緒態(ready):行程具備運行條件,等待系統分配 CPU 以便運行,
-
阻塞態 / 等待態(wait):行程不具備運行條件,正在等待某個事件的完成,

上圖中的時間片用完,可以這樣理解:
行程是并發執行的嘛,宏觀上在一段時間內能同時運行多個程式,但其實微觀上是交替發生的,也就是說 CPU 一般不會讓一個行程一次性執行完,為了保證所有行程可以得到公平調度,CPU 時間被劃分為一段段的時間片,這些時間片再被輪流分配給各個行程,某個行程的時間片用完后這個行程就會進入就緒態,而其他被分配到時間片的行程就會進入運行態,這個處于就緒態的行程就需要等待行程調度程式的下一次調度,為其分配 CPU 時間片后才能再次恢復運行,
需要注意的是:阻塞態是由于缺少需要的資源從而由運行態轉換而來,但是該資源不包括 CPU 時間片,缺少 CPU 時間片會從運行態轉換為就緒態,
很多系統中都增加了新建態(new)和終止態(exit),形成五態模型:
-
新建態(new):行程正在被創建時的狀態
-
終止態(exit):行程正在從系統中消失時的狀態

從上圖可以發現,只有就緒態和運行態可以相互轉換,其它的都是單向轉換,
這些不同狀態的行程作業系統是如何進行管理的呢?上文說過,PCB 是提供給作業系統使用的,是作業系統管理行程的主要依據,沒錯,操作就是通過 PCB 來管理這些擁有不同狀態的行程的,
行程的 PCB 會通過某種方式組織起來,一般來說,作業系統會把處于同一狀態的所有行程的 PCB 鏈接在一起,這種資料結構就稱為行程佇列(Process Queue),
④ 行程控制
所謂行程控制就是對系統中的所有行程實施有效的管理,實作行程狀態轉換功能,包括創建行程、阻塞行程、喚醒行程、終止行程等,這些功能均由原語來實作,作業系統通過原語來完成行程原理,包括行程的同步和互斥、行程的通信和管理,
什么是原語?原語是一種特殊的程式,它的執行具有原子性, 也就是說,這段程式的運行必須一氣呵成,不可中斷,原語是作業系統內核里的一段程式:

思考一下:為什么行程控制(行程狀態轉換)的程序要一氣呵成,不可中斷?
答:如果行程狀態轉換的程序不能一氣呵成,就有可能導致作業系統中的某些關鍵資料結構資訊不統一,這會影響作業系統進行別的管理作業,
行程的創建
作業系統初始啟動時會創建承擔系統資源分配和控制管理的一些系統行程,同時還會創建一個所有用戶行程的祖先,其他用戶行程是在應用程式運行時創建的,
作業系統允許一個行程創建另一個行程,而且允許子行程繼承父行程所擁有的資源,當子行程被終止時,其在父行程處繼承的資源應當還給父行程,同時,終止父行程時同時也會終止其所有的子行程,
創建行程的程序,也就是創建原語包含的內容如下:
-
在行程串列中增加一項,從 PCB 池中申請一個空閑的 PCB(PCB 是有限的,若申請失敗則創建失敗),為新行程分配一個唯一的行程識別符號;
-
為新行程分配地址空間,由行程管理程式確定加載至行程地址空間中的程式;
-
為新行程分配各種資源;
-
初始化 PCB,如行程識別符號、CPU 初始狀態等;
-
把新行程的狀態設定為就緒態,并將其移入就緒佇列,等待被調度運行,
什么事件會觸發行程的創建呢?有如下四種情況:
-
用戶登錄:分時系統中,用戶登錄成功,系統會為其建立一個新的行程
-
作業調度:多道批處理系統中,有新的作業放入記憶體中,會為其建立一個新的行程
-
提供服務:用戶向作業系統提出某些請求時,會新建一個行程處理該請求
-
應用請求:由用戶行程主動請求創建一個子行程
行程的終止
行程的終止也稱為撤銷,行程完成特定作業或出現嚴重錯誤后必須被終止,引起行程終止的事件有三種:
-
正常結束:行程自己請求終止(exit 系統呼叫)
-
例外結束:比如整數除 0,非法使用特權指令,然后被作業系統強行終止
-
外界干預:Ctrl + Alt + delete 打開行程管理器,用戶手動殺死行程
終止(撤銷)行程的程序,也就是撤銷原語包含的內容如下:
-
從 PCB 集合中找到終止行程的 PCB;
-
若行程處于運行態,則立即剝奪其 CPU,終止該行程的執行,然后將 CPU 資源分配給其他行程;
-
如果其還有子行程,則應將其所有子行程終止;
-
將該行程所擁有的全部資源都歸還給父行程或作業系統;
-
回收 PCB 并將其歸還至 PCB 池,
行程的阻塞和喚醒
行程阻塞是指行程讓出 CPU 資源轉而等待一個事件,如等待資源、等待 I/O 操作完成等,行程通常使用阻塞原語來阻塞自己,所以阻塞是行程的自主行為,是一個同步事件,當等待事件完成時會產生一個中斷,激活作業系統,在系統的控制下將被阻塞的行程喚醒,也就是喚醒原語,
行程的阻塞和喚醒顯然是由行程切換來完成的,
行程的阻塞步驟,也就是阻塞原語的內容為:
-
找到將要被阻塞的行程對應的 PCB;
-
保護行程運行現場,將 PCB 狀態資訊設定為阻塞態,暫時停止行程運行;
-
將該 PCB 插入相應事件的阻塞佇列(等待佇列),
行程的喚醒步驟,也就是喚醒原語的內容為:
-
在該事件的阻塞佇列中找到相應行程的 PCB;
-
將該 PCB 從阻塞佇列中移出,并將行程的狀態設定為就緒態;
-
把該 PCB 插入到就緒佇列中,等待被調度程式調度,
阻塞原語和喚醒原語的作用正好相反,阻塞原語使得行程從運行態轉為阻塞態,而喚醒原語使得行程從阻塞態轉為就緒態,如果某個行程使用阻塞原語來阻塞自己,那么他就必須使用喚醒原語來喚醒自己,因何事阻塞,就由何事喚醒,否則被阻塞的行程將永遠處于阻塞態,因此,阻塞原語和喚醒原語是成對出現的,
⑤ 行程背景關系切換
所謂行程的背景關系切換,就是說各個行程之間是共享 CPU 資源的,不可能一個行程永遠占用著 CPU 資源,不同的時候行程之間需要切換,使得不同的行程被分配 CPU 資源,這個程序就是行程的背景關系切換,一個行程切換到另一個行程運行,
因為行程是由內核進行管理和調度的,所以行程的背景關系切換一定發生在內核態,
行程背景關系的切換也是一個原語操作,稱為切換原語,其內容如下:
-
首先,將行程 A 的運行環境資訊存入 PCB,這個運行環境資訊就是行程的背景關系(Context)
-
然后,將 PCB 移入相應的行程佇列;
-
選擇另一個行程 B 進行執行,并更新其 PCB 中的狀態為運行態
-
當行程 A 被恢復運行的時候,根據它的 PCB 恢復行程 A 所需的運行環境
引起行程背景關系切換的事件,也就是某個占用 CPU 資源運行的當前行程被趕出 CPU 的原因有如下:
-
當前行程的時間片到
-
有更高優先級的行程到達
-
當前行程主動阻塞
-
當前行程終止
3. 執行緒基礎掃盲
① 什么是執行緒
結合文章開頭的簡單解釋,一個行程中可以有多個執行緒,它們共享這個行程的資源,
舉個例子,QQ 和 Chrome 瀏覽器是兩個行程,Chrome 行程里面有很多執行緒,例如 HTTP 請求執行緒、事件回應執行緒、渲染執行緒等等,執行緒的并發執行使得在瀏覽器中點擊一個新鏈接從而發起 HTTP 請求時,瀏覽器還可以回應用戶的其它事件,
② 為什么要引入執行緒
早期的作業系統都是以行程作為獨立運行的基本單位的,直到后期計算機科學家們又提出了更小的能獨立運行的基本單位,也就是執行緒,這就好比物理學家研究物質組成一樣:先發現了分子,然后繼續細分發現原子,再后來是原子核和電子、夸克等等,
那么,為什么要引入執行緒呢?我們只需要記住這句話:執行緒又稱為迷你行程,但是它比行程更容易創建,也更容易撤銷,
從上文我們知道,行程是擁有資源的基本單位,而且還能夠進行獨立調度,這就猶如一個隨時背著糧草的士兵,這必然會造成士兵的執行命令(戰斗)的速度,所以,一個簡單想法就是:分配兩個士兵執行同一個命令:一個負責攜帶所需糧草隨時供給,另一個士兵負責執行命令(戰斗),這就是執行緒的思想,輕裝上陣的士兵就是執行緒,

用嚴謹的語言描述來說就是:由于創建或撤銷行程時,系統都要為之分配或回收資源,如記憶體空間、I/O 設備等,需要較大的時空開銷,限制了并發程度的進一步提高,為減少行程切換的開銷,把行程作為資源分配單位和調度單位這兩個屬性分開處理,即行程還是作為資源分配的基本單位,但是不作為調度的基本單位(很少調度或切換),把調度執行與切換的責任交給執行緒,即執行緒成為獨立調度的基本單位,它比行程更容易(更快)創建,也更容易撤銷,
記住這句話!引入執行緒前,行程是資源分配和獨立調度的基本單位,引入執行緒后,行程是資源分配的基本單位,執行緒是獨立調度的基本單位,
③ 執行緒優缺點
執行緒的特征和行程差不多,行程有的他基本都有,比如:
-
執行緒具有就緒、阻塞、運行三種基本狀態,同樣具有狀態之間的轉換關系;
-
執行緒間可以并發執行
-
在多 CPU 環境下,各個執行緒也可以分派到不同的 CPU 上并行執行
執行緒的優點:
-
一個行程中可以同時存在多個執行緒,這些執行緒共享該行程的資源,行程間的通信必須請求作業系統服務(因為 CPU 要切換到內核態),開銷很大,而同行程下的執行緒間通信,無需作業系統干預,開銷更小,
不過,需要注意的是:從屬于不同行程的執行緒間通信,也必須請求作業系統服務,
-
執行緒間的并發比行程的開銷更小,系統并發性提升,
同樣,需要注意的是:從屬于不同行程的執行緒間切換,它是會導致行程切換的,所以開銷也大,
執行緒的缺點:
-
當行程中的一個執行緒奔潰時,會導致其所屬行程的所有執行緒奔潰,
舉個例子,對于游戲的用戶設計,就不應該使用多執行緒的方式,否則一個用戶掛了,會影響其他同個行程的執行緒,
4. 總結
作業系統的設計,從行程和執行緒的角度來說,可以歸結為三點:
-
以多行程形式,允許多個任務同時運行;
-
以多執行緒形式,允許單個任務分成不同的部分運行;
-
提供協調機制,一方面防止行程之間和執行緒之間產生沖突,另一方面允許行程之間和執行緒之間共享資源,
參考資料
-
《作業系統教程》— 第 5 版 費翔林
-
阮一峰:行程與執行緒的一個簡單解釋
http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html
?? 關注公眾號 | 飛天小牛肉,即時獲取更新
-
博主東南大學碩士在讀,利用課余時間運營一個公眾號『 飛天小牛肉 』,2020/12/29 日開通,專注分享計算機基礎(資料結構 + 演算法 + 計算機網路 + 資料庫 + 作業系統 + Linux)、Java 基礎和面試指南的相關原創技術好文,本公眾號的目的就是讓大家可以快速掌握重點知識,有的放矢,希望大家多多支持哦,和小牛肉一起成長 ??
-
并推薦個人維護的開源教程類專案: CS-Wiki(Gitee 推薦專案,現已 1.0k+ star), 致力打造完善的后端知識體系,在技術的路上少走彎路,歡迎各位小伙伴前來交流學習 ~ ??
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/263191.html
標籤:Java
上一篇:強參考、軟參考、弱參考、虛參考以及他們之間和 gc 的關系
下一篇:一個 Java 物件到底有多大?
