本文簡要介紹Linux調度機制以及如何在應用層進行調度優化。
進程分類
根據進程工作場景,可將進程分為
- 交互時進程:這類進程大部分時間都在等待輸入,CPU占用不高,要求響應迅速。例如:編輯器
- 批處理進程:進行大量密集計算以及IO,關心最終輸出結果,對響應時間以及資源要求較低
- 實時進程:硬實時,嚴格要求在指定時間內完成指定任務。軟件實時,盡可能快的完成任務。。
上述三類進程,從實時性視角上,分為實時進程和普通進程,具體取決於進程優先級數值。
調度器對這兩類進程有不同的調度方式:
- 實時進程,基於優先級隊列進行調度,優先級定義從0到99,0表示最高,99最低,共100個優先級。高優先級優於低優先級進程調度,相同優先級下,根據調度策略來調度。
- 普通進程,使用
CFS
調度器。在一個調度周期內,所有進程都有機會被調度,區別在於運行時間,與進程的nice
相關。
調度策略
調度策略有:
SCHED_FIFO
: 同等優先級,先入先出,調度觸發時機:- 進程主動調用
sched_yield
,讓出CPU
資源 - 進程被更高優先級進程搶占
- 進程停止或者被殺死
- 進程在等待鎖、睡眠(把調度實體從優先級隊列中刪掉,等待被喚醒后,再添加到優先級隊列中)
- 進程主動調用
SCHED_RR
: 同等優先級的進程,按時間片輪轉,調度時間片為100ms
,調度時機:- 包含
SCHED_FIFO
的4種情況 - 當前進程耗盡時間片(將調度實體移動到優先級隊列尾部)
- 包含
問題:那如果一直存在實時進程,會不會導致普通進程始終獲取不到CPU
資源?
解答:Linux
針對這種情況,提出組調度策略,限制實時進程的總運行時間,可通過以下兩個參數進行控制:
在 sched_rt_period_us
時間內,所有實時進程運行時間之和不得超過 sched_rt_runtime_us
的時間,如上圖所示,在1秒內,所有實時進程最多運行0.95秒,剩下的0.05秒留給普通進程。
針對普通進程,采用CFS
調度器,其核心思想是完全公平,調度策略為SCHED_OTHER
,新創建的線程,默認都是普通進程。
實踐前的准備
linux
中,應用層和內核層對優先級的定義不同。
優先級區分 | 實時進程 | 普通進程 |
---|---|---|
應用層優先級 | [1,99] 1最低,99最高 | |
內核優先級 | [0,99],0最高,99最低 | [100,139],100最高,139最低 |
應用層可通過如下兩種方式設置進程優先級:
nice
函數設置普通進程優先級,取值范圍為-20~19,設置值越大,優先級越低。pthread_setschedparam
設置實時進程優先級,設置的值越大,優先級越高。
默認情況下,創建的進程都是普通進程,通過top
命令查看進程狀態,與優先級相關的字段有PR和NI。
- PR列:給內核調度使用的優先級,如果該域顯示為
rt
,則表明該進程為實時進程 - NI列:進程的nice值,默認為0,取值范圍為[-20,19], 負值意味着更高的優先級
控制接口
多線程在內核看來是輕量級進程,以下都以進程來標識說明。調度機制由調度策略以及調度參數來控制,目前有兩套接口可供使用:
-
以
sched_
開頭的系統調用,例如,sched_setparam
、sched_setscheduler
,
對進程標識采用__pid_t
,使用getpid()
接口獲取,以應用層進程級別來控制。 -
以
pthread_XX
開頭的接口,由線程庫提供,例如,pthread_setschedparam
等,對進程標識采用pthread_t
,使用pthread_self
接口獲取,以應用層線程級別來控制。
以下采用pthread_XX
系列接口。
實踐
測試思路, 主線程創建兩個子線程1和2,每個子線程中循環執行耗時代碼,直到主線程通知它們退出,留給子線程的運行時間設置為5秒,測試代碼見文末鏈接。
場景1:兩個線程都是默認屬性
測試結果:兩個線程執行次數相差無幾,表明線程1和2平均得到了CPU資源。
場景2:通過nice增加線程1優先級,線程2保持不變。
測試結果:增加線程1的優先級,線程1的執行次數多余線程2的執行次數
場景3:通過nice降低線程1優先級,線程2保持不變。

測試結果:降低線程1的優先級,線程1的執行次數少於線程2的執行次數
場景3:設置線程1調度模式為FIFO
,線程2為默認屬性。
測試結果:線程1和線程2都有機會運行,但線程1運行次數多余2。通過查看運行時的綁定CPU,發現兩個線程在運行過程中會切換CPU,如果線程1和線程2綁定在同一個CPU上,FIFO
調度策略線程會一直占用CPU,直到它退出。
場景4:線程A和線程B綁定在同一個CPU上,線程A為FIFO模式,線程B保持默認。
測試結果:綁定在同一個CPU上后,線程A始終占據CPU資源,一直在運行,線程B得不到資源。FIFO這種模式會一直占用CPU,直到進程主動退出,即使有相同FIFO和相同優先級的進程,也不會搶占。
場景5:線程A和線程B綁定在同一個CPU上,線程A為RR模式,線程B也認為RR模式。兩種優先級一樣。
測試結果:先搶占到CPU的線程,始終占有CPU資源。測試結果和RR模式下時間片輪轉,相同優先級會均勻調度的結論不一樣。不知道什么原因?后來在線程中加入適當延時,主動讓出調度機會,得到的結果就符合預期。
場景6:線程中加入適當延時后的
優先級不同的兩個RR進程,高優先級的RR進程1始終占用CPU,直到進程1退出,進程2才獲取了1次執行機會。
如果兩個線程優先級設置為一致,則平均獲得CPU資源,結果如下圖:
小結
在多線程程序中,要在應用層提高線程優先級,可通過nice
函數提高線程靜態優先級,或者采用SCHED_RR
調度模式,同時,采用綁定CPU的方式來獨占某個核,降低cache失效概率,提高線程運行效率。
參考文檔:
- linux進程調度之 FIFO 和 RR 調度策略
- linux內核普通進程CFS調度原理
- linux線程調度策略
- 查看代碼運行在哪個CPU
- 測試代碼下載鏈接 ,提取碼: dkj3