在作業系統中,程式和執行緒是極為重要的概念,這篇文章主要總結了程式、執行緒的基本概念及程式的排程。
1. 程式
1.1 什麼是程式
程式(process
)是具有一定獨立功能的程式關於某個資料集合上的一次執行活動。在傳統 OS
中,程式是系統進行資源分配和排程的基本單位。程式是一個正在執行程式的例項,包括程式程式碼、程式計數器和暫存器的值以及系統資源(如開啟的檔案)等。
在某一瞬間,一個 CPU
中只能執行一個程式,它是在各個程式之間來回切換的,每個程式執行的速度也不確定。
程式和程式間的聯絡和區別如下:
- 程式是一段靜態的程式碼;程式是一個動態執行的過程;
- 程式是產生程式的基礎;程式是程式功能的體現;
- 程式的每次執行都構成了不同的程式,通過多次執行,一個程式可對應多個程式;通過呼叫關係,一個程式可包括多個程式;
1.2 程式的實現
為了實現程式,作業系統維護了一張程式表(結構陣列),每個程式佔用一個程式表項(程式控制塊 PCB
),它是程式存在的唯一標誌。該表項包含了程式狀態的重要資訊,包括程式識別符號、狀態、優先順序、程式計數器、堆疊指標、暫存器等;
對程式的管理就是對通過 PCB
的組織管理來實現的。由於儲存了在程式的狀態變化時的必要資訊,在中斷一個正在執行的程式,並在後來恢復時,就好像程式從未中斷過。它是支援多程式和提供多處理的關鍵。
1.3 程式的狀態及轉換
如下圖是程式的三種狀態的狀態圖。一個程式的三種狀態是:
- 執行態:程式正在佔用
CPU
執行; - 就緒態:程式處於準備執行狀態,已經獲得除
CPU
外的所有資源; - 阻塞態:程式由於等待某種外部事件而暫停執行,即使獲得
CPU
,也不能執行。
程式之間主要有四種轉換關係:
- 由執行態轉換為就緒態:在時間片用完後,不得不讓出
CPU
; - 由就緒態轉換為執行態:程式被排程,獲得處理機資源;
- 由阻塞態轉換為就緒態:程式的
I/O
請求完成; - 由執行態轉換為阻塞態:程式發出
I/O
請求;
2. 執行緒
2.1 什麼是執行緒
執行緒(thread
)是程式中的一條執行流程。在引入執行緒的作業系統中,程式是擁有資源的基本單位;而執行緒是 CPU
排程和分派的基本單位。
一個程式中可以有多個執行緒,多個執行緒可以併發執行,它們之間共享相同的地址空間。但如果一個執行緒崩潰,可能會導致其所屬程式的所有執行緒崩潰。
2.2 程式和執行緒的區別
- 排程性:傳統 OS 中,程式是排程和分派的基本單位,但在引入執行緒後,執行緒是排程和分派的基本單位;
- 併發性:在引入執行緒後,多個程式可以併發執行,一個程式中的多個執行緒也可以併發執行;
- 擁有資源:程式是擁有資源的基本單位,而執行緒本身並不擁有資源;
- 系統開銷:程式切換付出的系統開銷明顯大於執行緒;
2.3 執行緒實現的三種方式
有三種執行緒的實現方式:
- 使用者執行緒:使用者空間實現,由使用者執行緒庫管理;
- 核心執行緒:核心中實現,由作業系統管理;
- 輕量級程式:核心中實現,支援使用者執行緒;
使用者執行緒
使用者執行緒是把整個執行緒包放在使用者空間中,不依賴於作業系統的核心,所以它可以在不支援執行緒的作業系統上實現。可以用一組使用者級的執行緒函式庫來實現執行緒。
每個程式都需要私有的執行緒表,用來跟蹤記錄該程式中執行緒的狀態資訊,不過僅記錄每個執行緒的程式計數器、堆疊指標、暫存器和狀態等,該執行緒表由執行時系統管理。而且使用者執行緒的切換由執行緒庫函式來完成,不需要使用者態、核心態切換,所以執行緒排程速度特別快。另外,也允許每個程式都擁有自定義的執行緒排程演算法。
但如果一個執行緒發起系統呼叫而阻塞,儘管其他執行緒可以執行,但整個程式都會阻塞。當一個執行緒開始執行後,除非它主動較交出 CPU
,否則它所在的程式中的其他執行緒將無法執行。
核心執行緒
核心執行緒在作業系統的核心中實現,由核心來完成對執行緒的建立、終止和管理。
由於執行緒的建立、終止和切換通過系統呼叫執行,由核心完成的,其系統開銷比較大;
但在一個程式中,如果某個核心執行緒發起系統呼叫而被阻塞,並不會影響其他使用核心執行緒的執行;
輕量級程式
輕量級程式是核心支援的使用者執行緒。一個程式可以有一個或多個輕量級程式,每個輕量級程式由一個單獨的核心執行緒來支援。而輕量級程式內部可以對應多個使用者執行緒。
3. CPU 排程
3.1 排程概念
處理機排程是當有多個程式(執行緒)競爭 CPU
時,排程程式需要從從就緒佇列中挑選下一個佔用 CPU
執行的程式。
排程演算法是為了解決通過什麼樣的準則來挑選就緒對列中的哪一個程式來執行。在每次排程時需要決定在下一個 CPU
計算時將哪個程式交給 CPU
。
3.2 排程演算法
先來先服務演算法FCFS
根據程式進入就緒態的先後順序排列。當程式進入阻塞態或結束時,就緒佇列中的下一個程式佔用 CPU
。
實現簡單,但可能短程式排在長程式後面,導致平均等待時間波動較大。
短程式優先演算法SJF
選擇就緒佇列中執行時間最短的程式佔用 CPU
執行。
- 可搶佔系統改進:短剩餘時間優先演算法,即選擇剩餘執行時間最短的程式執行。
短程式優先演算法有最優的平均週轉時間,但連續的短程式可能會使長程式無法獲得 CPU
資源,導致飢餓;執行時間不可預估,並不可靠。
最高響應比優先演算法HRN
選擇就緒佇列中響應比最高的程式。它是基於短程式優先演算法的改進,它不允許搶佔,另外等待時間越長,響應比越高,可以避免長時間地等待。
R = (w+s)/s // 其中 w 為等待時間,s 為執行時間。
複製程式碼
時間片輪轉演算法RR
按時間片分配給程式執行。在輪轉中,每個程式分到執行 1/n
的時間,時間片結束時,按先來先服務演算法切換到下一個就緒程式,每隔 n-1
個時間片程式會再次執行。
如果時間片過大,程式等待時間過長,極限情況下會退化為先來先服務演算法;如果時間片過小,雖然反應迅速,但上下文切換開銷較大,會影響系統吞吐量。
多級反饋佇列演算法
就緒佇列被劃分為多個獨立的子佇列,而且每個佇列可以有自己的排程策略,在佇列之間可以設定優先順序,第一個佇列優先順序最高,其餘依次遞減。優先順序越高的佇列分配的時間片越短。
在執行時,程式在不同佇列間移動,如果程式在當前優先順序的時間片下沒有完成,則下降到低一優先順序的佇列,以此類推。只有當一個佇列為空時才會去執行下一個佇列中的程式。
這種演算法對於 CPU
密集型程式的優先順序下降很快,而 I/O
密集型程式停留在高優先順序。