引言
當我們在平時工作或學習中用電腦的時候,我想很多人都是一邊聽着音樂一邊寫代碼、看文檔、編輯Markdown寫筆記或者Chrome看着網頁,不知道你有沒有好奇電腦爲什麼可以可以同時完成這些程序的運行,並且如果你細心可能發現了有時候程序的響應快,有時候響應慢。這些都是因爲進程&線程調度的結果,哪怕你的電腦處理器只有一核,可同時同時執行多個應用程序。
爲何需要調度
並不是所有的程序都是cpu密集型的,也有I/O密集型,如下圖,a是cpu密集型任務,它有很長的cpu週期,而b則持有cpu的時間短但是卻很頻繁。如果你是系統的設計者你會讓程序如果運行呢?如果每次都先執行完a再執行b,那麼磁盤可能長期處於空閒狀態,這裏有一個簡單的想法是,優先保證b進程執行,這樣磁盤就可以長期保持忙碌的狀態。所以,進程調度的目的一方面是讓程序併發執行,一方面是爲保持系統各個組件處於忙碌狀態避免資源浪費。
下面的筆記是探索進程&線程調度的筆記,一步步揭開處理器調度神祕的面紗。本文的結果,先介紹一些調度的基本概念方便理解後邊的內容,然後會介紹調度算法(以單核和進程爲視角介紹,線程和進程差不多的),之後會介紹線程調度的特有特徵,然後介紹多核心環境下調度,最後可能會簡單的舉個例子介紹實際OS採用的是什麼調度策略。
基本概念
調度器
調度器是操作系統實現的,通過一定的算法從一堆就緒線程中選擇一個,然後完成上下文切換,然後執行新調度線程的代碼的一個線程,注意調度器也是一個線程,它是一個高優先級的內核線程。
搶佔式與非搶佔式
- 非搶佔式:挑選一個進程一直運行,直到進程阻塞(I/O或等待其他線程)或者自願讓出cpu
- 搶佔式:挑選一個進程執行固定的時間,當進程用完最大時間還在執行時會被掛起,然後切換另一個就緒的進程,並且當一個比當前運行進程更高優先級進程到達時,可以搶佔當前進程的cpu
何時調度
- 在創建新進程後,需要決策時運行父進程還是子進程
- 在一個進程退出時
- 當一個進程被阻塞在I/O、信號量或者其他原因阻塞時,必須選擇另一個進程運行
- 在一個I/O中斷時
調度環境
- 批處理:適合非搶佔式算法,減少進程切換
- 交互式:搶佔是必須的,不能讓一個進程一直霸佔CPU
- 實時:對deadline要求嚴格
調度算法的目標
- 公平:相似的進程應得到相應的服務
- 策略強制執行
- 平衡:保持系統各個部分儘可能忙碌
- 批處理三個指標
- 吞吐量:最大化每小時運行工作數
- 週轉時間:任務提交到運行結束的平均統計時間,時間越小越好
- CPU利用率:保持cpu在所有時間內忙綠
- 交互式系統指標
- 響應時間:快速響應用戶請求
- 均衡:滿足用戶期望
- 實時系統
- 滿足截止時間:避免錯過數據
- 可預測性:避免在多媒體系統中品質降低
批處理系統調度算法
Frist-Come Frist-Served(FCFS)
- 原理:顧名思義,就是先到的進程先調度,假設進程到達的順序是p1->p2->p3
p2等待時間是24ms,p3等待時間是27ms,所以平均等待時間17ms,現在假設進程到達的順序是p2->p3->p1那麼如下圖,平均等待時間是3ms
- 缺點
- 平均等待時間長
- 不能很好平衡CPU和I/O的利用率, 如果遇到計算密集型任務(會長期霸佔cpu),可能導致I/O空閒
最短作業優先(SJF)
- 原理:每次調度都選擇下一次執行時間最短的作業執行,假設4個進程的需要的cpu突發時間如下圖
都是就緒狀態那麼調度順序將如下圖
平均等待時間爲7ms,如果用FCFS算法,平均等待時間可能爲10.25ms - 缺點
- 只有在進程可以同時就緒時,SJF纔是最優的
- 無法獲得下一次進程的cpu突發時間,可以利用一種指數平均方法來估計下一次cpu突發時間
代表第n+1次的預測值,代表第n次實際時間,代表歷史實際突發時間權重,,通常取
最短剩餘時間優先
- 原理:基本思想和SJF相同,但是多了一點,最短剩餘時間優先是可以搶佔的,假設進程的到達時間和cpu突發時間如下圖
實際調度順序
p1最先到達,但是p2在p1執行1時到達,因爲p2的cpu突發時間比p1剩餘的cpu突發時間更短,因此p2搶佔p1的進程。平均等待時間是。
交互式系統調度算法
輪轉調度(Round-Robin Scheduling RR)
- 原理:類似FCFS算法,添加了搶佔和時間片概念。爲每一個進程分配一個時間片運行,在時間片結束前阻塞或結束,則CPU立即進行切換,或者進程用完分配的時間片,進程被掛起,切換就緒隊列的頭運行。時間片長度一般從10-100ms。就緒隊列可以看成是一個循環隊列,每個用完時間片的進程又加入到隊列尾部,結果如下圖
- 一個簡單的例子,假設進程cpu突發時間爲10,下圖是時間片爲1,6,12時,上下文切換的次數,可以看出時間片越短,上下文切換次數越多
- 缺點
- 時間片太長,RR算法可能會退化成FCFS
- 時間片太短,如上圖,上下文切換的次數將增加,進程週轉時間越大。
浪費時間 = 上下文切換時間 / 時間片
優先級調度(Priority Scheduling)
- 原理:每個進程都被分配了一個優先級,cpu將會被分配給高優先級的進程,同等優先級的進程採用FCFS算法調度
- 可搶佔優先級調度:當一個新的進程到達且優先級比當前cpu執行的進程高時,新進程可以搶佔當前進程的cpu
- 非可搶佔優先級調度:當一個新的進程到達且優先級比當前cpu執行的進程高時,只是簡單的將新進程加入隊列的頭部
- 一個簡單例子,假設一個p1,p2…p5集合在時間0到達,cpu突發時間和優先級如下
它們的調度順序如下圖
- 缺點:飢餓,低優先級的進程可能無限期的等待,或者高優先級的進程一直霸佔CPU,有幾種解決方法
- 對於低優先級進程可以引入老化,定期提升一直未獲得cpu的低優先級進程的優先級。比如假設優先級範圍爲0-127,定時時間爲1s,一個最低優先級127的進程,只需要2min左右的時間就可以提升0
- 結合輪轉調度,同一優先級的進程會採用輪轉調度,比如
它們的調度順序會是這樣
多級隊列調度
- 原理:按不同進程的特徵,劃分爲不同的類別,然後不同類別的進程有不同的優先級,有獨立的調度隊列,採用不同的調度算法,比如
舉個例子,首先實時任務的優先級最高,因爲實時任務有deadline要求,其次是系統任務,然後是交互式任務,最低優先級爲批處理。每個優先級有自己的調度算法,比如交互式任務可以用RR調度,而批處理任務可以用FCFS調度
多級反饋隊列調度
- 原理:多級反饋隊列調度同樣是對於不同優先級的進程有多個隊列,進程在隊列之間是可以移動的,它的核心思想是CPU密集的進程會逐漸移動到低優先級隊列,而I/O密集和交互式進程可以在高優先級隊列保持低延遲響應。爲了防止飢餓,長時間等待的低優先級進程可以升級到高優先級隊列。每級隊列可以採用獨立的調度算法。
- 簡單舉個例子,如下圖,有三級隊列,每個新來的進程會進入0級隊列,假設此時有1級隊列的進程正在執行,則會被新進程搶佔cpu。0、1級隊列都採用RR調度算法,2級採用FCFS調度算法,當0級的進程時間片用完後將降級加入1級隊列
- 多級反饋隊列調度關鍵參數
- 隊列數
- 每級隊列的調度算法
- 決策提升優先級的方法
- 決策降低優先級的方法
- 決策進程進入哪級隊列的方法
實時系統調度算法
實時系統和批處理和交互式系統不同,這裏先列出一些實時系統的基本概念
基本概念
- 實時系統分類
- 軟實時系統:雖然也有時限要求,但是並不保證在deadline執行,錯過時限也不會導致嚴重的後果
- 硬實時系統:硬實時系統,嚴格的時限要求,任務必須在deadline執行,錯過了deadline意味着嚴重的後果
- 最小延遲
- 事件延遲,從事件的發生到事件響應的事件
- 中斷延遲:cpu收到中斷時,要做兩件事,第一檢查中斷類型,第二上下文切換保存當前正在執行線程的狀態,這段時間就是中斷延遲。有一個重要因素是當內核數據結構更新時是禁止響應中斷的,因此實時系統要求禁止響應中斷的時間非常短
- 分派延遲
- conflicts階段
- 搶佔正在運行的進程
- 低優先級進程釋放高優先級進程需要的資源
- dispatch階段:調度高優先級進程到可用的cpu
- conflicts階段
- 中斷延遲:cpu收到中斷時,要做兩件事,第一檢查中斷類型,第二上下文切換保存當前正在執行線程的狀態,這段時間就是中斷延遲。有一個重要因素是當內核數據結構更新時是禁止響應中斷的,因此實時系統要求禁止響應中斷的時間非常短
- 事件延遲,從事件的發生到事件響應的事件
基於優先級的調度
- 原理:給實時任務分批較高的優先級
- 缺點:基於優先級的搶佔式調度算法只能保證軟實時
Rate-Monotonic Scheduling(RMS)
- 基本假設
- 假設調度週期性任務
- 假設每個任務進入系統會被分配一個固定優先級,週期越短優先級越高,週期越長優先級越低
- 假設任務的deadline是下一次執行前
- 實際例子分析
- 假設第一個進程P1週期爲50,cpu時間爲20,第二個進程P2週期爲100,cpu時間爲35
- 計算cpu利用率
- 假設p2先運行,那麼p1就無法在deadline50完成
- 假設p1先運行
- 計算cpu利用率
- 假設第一個進程P1週期爲50,cpu時間爲25,第二個進程P2週期80,cpu時間爲35
- 計算cpu利用率,cpu利用率96%感覺可以運行,接下來分析一下調度
- 無論如何調度,都無法滿足
- 計算cpu利用率,cpu利用率96%感覺可以運行,接下來分析一下調度
- 假設第一個進程P1週期爲50,cpu時間爲20,第二個進程P2週期爲100,cpu時間爲35
- 結論:不能利用cpu最大利用率來確定是否能成功調度,應該取調度N個進程時最差的cpu利用率,當需要調度的任務cpu利用率超過該值時,無法調度,由此可見,RMS調度算法cpu利用率較低。N個進程最差的cpu利用率計算公式:
Earliest-Deadline-First Scheduling(EDF)
- 原理:根據任務的deadline動態調整進程的優先級。理論上是最優的算法,既能保證進程滿足deadline又能保證cpu有很好的利用率
- 舉個簡單的例子,假設第一個進程P1週期爲50,cpu時間爲25,第二個進程P2週期80,cpu時間爲35
- 和RMS比較
- 不需要任務是週期性任務
- 不需要任務cpu執行時間是常數
Proportional Share Scheduling(PSS)
- 原理:按比例分享cpu時間,admission-control會接收一個請求如果有足夠的比例份額,否則會拒絕請求,假設A需要50、B需要20、C需要15,當新來的D需要30,則會被拒絕
線程調度
上面所有算法都是以進程爲調度目標是爲了簡化,線程調度算法和進程的是一樣的,其實現代操作系統調度的都是內核線程。下面是上邊沒有介紹到的線程調度的區別
競爭範圍
- PCS:進程競爭範圍,在many-to-one和many-to-many線程模型中,用戶級線程調度器調度用戶線程運行在一個可用的LWP上,當用戶線程獲得可用LWP並不意味着該線程正運行在cpu上,還要取決於LWP的內核線程是否被調度獲得物理CPU
- SCS:系統競爭範圍,所有內核線程競爭cpu
多處理器調度
多處理器的種類
- 多核心
- 多線程內核(Intel的超線程)
- NUMA系統
- 異構多核處理器(ARM,多爲移動設備芯片)
非對稱多處理器
- 所有的調度、i/o和其他系統活動處理都在同一個處理器上(master),其他處理器負責執行用戶代碼。
- 優點,只有master處理器訪問系統數據,減少數據共享
- 致命缺點:master處理器成爲瓶頸
對稱多處理器(SMP)
- 所有處理器自調度
- 調度隊列
- a共用一個隊列,導致的問題是queue的競態問題,需要同步操作,否則可能兩個core取了同一個thread,那麼queue的鎖成了性能的瓶頸
- b最通用的方法,解決了通用queue鎖的瓶頸問題,而且可以有效利用緩存。有一個問題是工作量均衡問題,可用通過一個平衡算法來均衡各個處理器的工作量
多核心處理器memory stall問題
- 當處理器執行代碼需要訪問主存時,因爲訪問主存很慢,導致會浪費很多cpu週期
- 解決辦法:芯片多線程技術(CMT),例如Intel的超線程,也就是我們平時總看到的i5雙核四線程,就是每個核心有兩個hardware thread
CMT處理器的結構,hardware thread共享物理芯片資源,比如緩存和流水線
CMT實現- 粗粒度:發生高延遲時切換hardware thread,上下文切換需要刷新指令流水線,因此開銷高
- 細粒度:在更細的級別上,比如在指令週期的邊界
實際兩級調度
- 簡單輪轉調度算法:Ultra SPARC
- urgency優先級,每次切換時比較哪個優先級更高:Intel Itanium
負載均衡
- Push migration:一個特殊任務定期檢查,如果檢查到不平衡,就會將線程從高負載的cpu移到空閒或低負載的cpu
- Pull migration:當cpu空閒時會主動從高負載的cpu拉去任務
- 負載均衡會導致一個問題,當一個已經執行過的線程因爲負載均衡被遷移到其他cpu時,其緩存將失效,需要的數據需要重新從主存加載
處理器親和
- 定義:處理器親和指的是一個線程在一個處理器上運行,當處理器再次調度它時,有緩存的優勢,如果它再次執行的時候被遷移到其他cpu執行,那麼之前cpu的緩存將失效,並且當前cpu需要重新從主存中加載緩存,因此處理器親和的優勢可能會被負載均衡抵消
- 軟親和:系統試圖保證現在在同一個cpu上調度,但是不能保證
- 硬親和:系統提供了系統調用使進程可以指定一個cpu子集供進程的線程運行
- 處理器親和在NUMA系統中非常重要,因爲每個cpu芯片有自己本地主存,訪問遠程主存是很慢的
案例:Linux調度
分類調度
- 默認的完全公平調度(CFS)(內核2.6.23)
- 兩個概念
- nice value:範圍-20~+19,用來映射優先級,值越小優先級越高
- virtual run time:虛擬運行時間,當nice value爲0,該時間爲物理運行時間;當nice value越大,說明優先級越低,cpu時間比例越小,那麼該時間大於真實運行時間;反之優先級越高,cpu時間比例越高,該時間小於真實運行時間
- 不採用固定時間片,而是採用比例,這個比例取決於nice value,值越小優先級越高,權重越高,調度週期內獲得cpu時間比例越高,也就是連續執行的時間替代時間片
- 調度: 選擇虛擬運行時間最小的調度,因此nice value越小,優先級越高,得到調度的機會越多,就緒隊列實現是利用red-black Tree,當一個任務變成不可運行(例如阻塞)會從樹中刪除
- 兩個概念
- 實時調度
- Linux分了兩個獨立的優先級範圍分別給實時任務(0-99)和普通任務(100-139,映射nice value~20-100,19-139)
負載均衡
- 在NUMA架構中,採用分層調度域(一個cpu核心集合),負載均衡不會遷移線程到其他域,防止線程遠程訪問主存