Linux程式排程核心實現分析
本文根據兩篇部落格總結,它們的地址分別是:
http://blog.csdn.net/giantpoplar/article/details/51791002
http://blog.csdn.net/giantpoplar/article/details/51791002
目錄
所謂搶佔
- 搶佔式
- 排程程式決定程式何時停止,為其他程式騰出 CPU 資源,這種強制掛起行為就是搶佔。程式被搶佔前執行時間為提前設定好的時間片。
- 非搶佔
- 程式佔據 CPU 資源不放,除非自己主動呼叫 schedule() 函式,這稱為程式讓步。
什麼是排程
排程的原因:現代作業系統都是多工的,為了能讓作業系統更好的發揮多工的特性,需要一個管理程式來管理所有任務,這個程式就是排程程式。
排程程式的功能有:
- 負責讓那些程式執行,那些程式等待
- 決定每個程式執行多長時間
此外為了更好地使用者體驗,執行中的程式還可以立即被其他更緊急的程式打斷。
總之排程是一個平衡的過程。一方面,它要保證各個執行的的程式能夠最大限度使用CPU(即儘量少的切換程式),另一方面,保證各個程式公平使用CPU(即防止一個程式長時間獨佔CPU的情況)
排程實現原理
排程主要基於程式優先順序和時間片實現。
程式的優先順序
程式的優先順序有兩種度量方法,一種是nice值,一種是實時優先順序。
nice 值的範圍是 -20~+19,值越大優先順序越低,也就是說 nice 值為-20的程式優先順序最大。
實時優先順序的範圍是 0~99,與 nice 值的定義相反,實時優先順序是值越大優先順序越高。
實時程式都是一些對響應時間要求比較高的程式,因此係統中有實時優先順序高的程式處於佇列的話,它們會搶佔一般程式的執行時間。
程式的實時優先順序高於 nice 值,在核心中,實時優先順序的範圍是 0~MAX_RT_PRIO-1。
MAX_RT_PRIO的定義參見 include/linux/sched.h
#define MAX_USER_RT_PRIO 100
#define MAX_RT_PRIO MAX_USER_RT_PRIO
nice值在核心中的範圍是 MAX_RT_PRIO~MAX_RT_PRIO+40 即 MAX_RT_PRIO~MAX_PRIO
#define MAX_PRIO (MAX_RT_PRIO + 40)
一個程式有隻能擁有實時優先順序和 nice 值其中之一。
時間片
有了優先順序,就可以決定誰先執行了。但是對於排程程式來說,並不是執行一次就結束了,必須知道間隔多久進行下次排程。
於是就有了時間片的概念。時間片表示一個程式搶佔前能持續執行的時間。
時間片設定,過大會讓系統響應變慢;過小會產生由於程式頻繁切換帶來的開銷。
預設的時間片一般為 10ms。
排程的實現原理
基本步驟我的理解是:
- 首先確定每個程式佔用多少 CPU 時間,如利用 nice 值進行計算。
- 讓佔用 CPU 時間多的先執行。(讓列寧同志先走)
- 執行完後,扣除執行程式的 CPU 時間,接下來繼續重複這三步。
Linux上排程實現的方法
Linux 上採用了“完全公平排程演算法”,簡稱“CFS”。
CFS 演算法在分配給每個程式 CPU 時間時,不是分配給它們一個 CPU 的絕對時間,而是根據程式的優先順序分配給它們一個佔用 CPU 時間的百分比。
舉例:
程式名 | nice值 | 百分比 |
---|---|---|
程式A | 1 | 10% |
程式B | 3 | 30% |
程式C | 6 | 60% |
由上圖可知,佔據百分比之和就是 100%。所以我的理解是 Linux 下 CFS 演算法的主要步驟有:
- 計算每個程式的 vruntime,通過 update_curr() 函式更新程式的 vruntime。
- 選擇具有最小 vruntime 的程式投入執行。
- 程式執行完後,更新 vruntime,再回到步驟 2。
vruntime 變數存放程式的虛擬執行時間,該執行時間(花在執行上的時間和)是通過對可執行總數的加權後得出的,單位是 ns。計算權重實際上就是計算佔用 CPU 的百分比,通過它得出 vruntime,用來記錄一個程式執行了多長時間以及還應執行多久。
- 時間記賬
使用 sched_entity 結構體進行記賬,該結構體在 task_struct 中被包含。vruntime 作用上面說過了。 - 程式選擇
使用紅黑樹存放程式佇列,以 vruntime 為鍵值,每次選擇 vruntime 最小的程式。這裡有用到了一個 trick,使用 leftmost 快取了紅黑樹的最左葉子節點,提高效率(STL也這麼幹了)。 - 排程器入口
schedule() 函式呼叫 pick_next_task() 函式,按照優先順序從大到小一次檢查每一個排程類,從最高優先順序排程類中,選擇最高優先順序程式。 - 睡眠和喚醒
睡眠:程式標記為 TASK_INTERRUPTIBLE 或 TASK_UNINTERRUPTIBLE 狀態,從紅黑樹中移除,放入等待佇列,呼叫 schedule 選擇和執行一個其他的程式。
喚醒:通過 wake_up() 函式喚醒,程式標記為 TASK_RUNNING 狀態,從等待佇列放入紅黑樹中。
上下文切換
- switch_mm() 函式把虛擬記憶體從上一個程式切換到新程式。
- switch_to() 從上一個處理器狀態切換到新處理器狀態,儲存回覆棧暫存器資訊。
呼叫schedule()的時機
- 某個程式應該被搶佔,scheduer_tick 設定為 need_resched。
- 一個優先順序高的程式進入可執行狀態,try_wake_up 設定為 need_resched。
- 從核心返回使用者空間或從中斷返回,核心檢查 need_resched。如果已經設定,核心會在繼續執行之前呼叫排程程式
搶佔時機
使用者搶佔:
- 從系統呼叫返回使用者空間。
- 從中斷處理程式返回使用者空間。
核心搶佔:
- 中斷處理程式正在執行,且返回核心空間之前。
- 核心程式碼再一次具備可搶佔性的時候(我認為就是出於臨界區之外時)。
- 如果核心中的程式碼顯示呼叫 schedule()。
- 如果核心中任務阻塞。
實時排程
SCHED_FIFO:不使用時間片,先入先出。只有更高優先順序別的SCHED_FIFO,SHED_RR 程式才能搶佔,同一級別不能搶佔。
SCHED_RR:帶有時間片,實時輪流排程。時間片耗盡,允許同一級別程式搶佔。
相關文章
- 100行程式碼實現React核心排程功能行程React
- Linux 核心排程器原始碼分析 - 初始化Linux原始碼
- Linux核心學習筆記(5)– 程式排程概述Linux筆記
- linux程式排程Linux
- LInux實驗 : 程式排程模擬Linux
- Linux程式排程邏輯與原始碼分析Linux原始碼
- OS_程式排程:C++實現C++
- 程式排程案例分析與常見疑惑1:為何不能排程?
- OPENMP FOR CONSTRUCT GUIDED 排程方式實現原理和原始碼分析StructGUIIDE原始碼
- OpenMP For Construct dynamic 排程方式實現原理和原始碼分析Struct原始碼
- Kubernetes 排程器實現初探
- 排程器簡介,以及Linux的排程策略Linux
- Linux IO排程方法Linux
- asyncio系列之抽絲剝繭分析事件排程的核心原理事件
- linux核心--使用核心佇列實現ringbufferLinux佇列
- 深入工作流排程的核心
- haipproxy核心校驗和排程策略AI
- linux搶佔式排程Linux
- Linux I/O排程器Linux
- linux核心設計與實現Linux
- PHP 程式池與輪詢排程演算法實現多工PHP演算法
- verilog的RR輪詢排程演算法的程式碼實現演算法
- 調研:如何基於Linux平臺實現自主設計的排程器Linux
- 使用Java實現定時任務排程Java
- 簡版排程中心搭建及實現思路
- [典藏版] Golang 排程器 GMP 原理與排程全分析Golang
- Linux 定時任務排程Linux
- Linux中什麼情況下會發生程式排程?Linux
- linux中設定程式排程的優先順序別Linux
- Flink排程之排程器、排程策略、排程模式模式
- 實現Quartz.NET的HTTP作業排程quartzHTTP
- celery 與 flask 實現非同步任務排程Flask非同步
- 深入 Java Timer 定時排程器實現原理Java
- 《Linux核心設計與實現》學習【5】—— 核心同步Linux
- Linux核心技術分析Linux
- Linux程序排程器-CPU負載Linux負載
- Linux Shell指令碼時間排程Linux指令碼
- golang實現併發爬蟲三(用佇列排程器實現)Golang爬蟲佇列
- 從原始碼分析 GMP 排程原理原始碼