Linux調度機制以及應用層的優化


本文簡要介紹Linux調度機制以及如何在應用層進行調度優化。

進程分類

根據進程工作場景,可將進程分為

  • 交互時進程:這類進程大部分時間都在等待輸入,CPU占用不高,要求響應迅速。例如:編輯器
  • 批處理進程:進行大量密集計算以及IO,關心最終輸出結果,對響應時間以及資源要求較低
  • 實時進程:硬實時,嚴格要求在指定時間內完成指定任務。軟件實時,盡可能快的完成任務。。

上述三類進程,從實時性視角上,分為實時進程和普通進程,具體取決於進程優先級數值。
調度器對這兩類進程有不同的調度方式:

  • 實時進程,基於優先級隊列進行調度,優先級定義從0到99,0表示最高,99最低,共100個優先級。高優先級優於低優先級進程調度,相同優先級下,根據調度策略來調度。
  • 普通進程,使用CFS調度器。在一個調度周期內,所有進程都有機會被調度,區別在於運行時間,與進程的nice相關。

調度策略

調度策略有:

  • SCHED_FIFO: 同等優先級,先入先出,調度觸發時機:
    • 進程主動調用sched_yield,讓出CPU資源
    • 進程被更高優先級進程搶占
    • 進程停止或者被殺死
    • 進程在等待鎖、睡眠(把調度實體從優先級隊列中刪掉,等待被喚醒后,再添加到優先級隊列中)
  • SCHED_RR: 同等優先級的進程,按時間片輪轉,調度時間片為100ms,調度時機:
    • 包含SCHED_FIFO的4種情況
    • 當前進程耗盡時間片(將調度實體移動到優先級隊列尾部)

問題:那如果一直存在實時進程,會不會導致普通進程始終獲取不到CPU資源?
解答:Linux針對這種情況,提出組調度策略,限制實時進程的總運行時間,可通過以下兩個參數進行控制:

Linux組調度策略.jpg

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_setparamsched_setscheduler
    對進程標識采用__pid_t,使用getpid()接口獲取,以應用層進程級別來控制。

  • pthread_XX開頭的接口,由線程庫提供,例如,pthread_setschedparam等,對進程標識采用pthread_t,使用pthread_self接口獲取,以應用層線程級別來控制。

以下采用pthread_XX系列接口。

實踐

測試思路, 主線程創建兩個子線程1和2,每個子線程中循環執行耗時代碼,直到主線程通知它們退出,留給子線程的運行時間設置為5秒,測試代碼見文末鏈接。

場景1:兩個線程都是默認屬性

場景1——都是普通屬性.jpg

測試結果:兩個線程執行次數相差無幾,表明線程1和2平均得到了CPU資源。

場景2:通過nice增加線程1優先級,線程2保持不變。

場景2_增加A線程的優先級.jpg

測試結果:增加線程1的優先級,線程1的執行次數多余線程2的執行次數

場景3:通過nice降低線程1優先級,線程2保持不變。

![場景2_降低A線程的優先級.jpg](https://i.loli.net/2021/01/21/
G8zwgouAs4JM52e.jpg)

測試結果:降低線程1的優先級,線程1的執行次數少於線程2的執行次數

場景3:設置線程1調度模式為FIFO,線程2為默認屬性。

測試結果:線程1和線程2都有機會運行,但線程1運行次數多余2。通過查看運行時的綁定CPU,發現兩個線程在運行過程中會切換CPU,如果線程1和線程2綁定在同一個CPU上,FIFO調度策略線程會一直占用CPU,直到它退出。

場景3-線程1設置為FIFO模式.jpg

場景4:線程A和線程B綁定在同一個CPU上,線程A為FIFO模式,線程B保持默認。

場景4-線程1和線程2綁定在同一個CPU上,線程1設置FIFO屬性.jpg

測試結果:綁定在同一個CPU上后,線程A始終占據CPU資源,一直在運行,線程B得不到資源。FIFO這種模式會一直占用CPU,直到進程主動退出,即使有相同FIFO和相同優先級的進程,也不會搶占。

場景5:線程A和線程B綁定在同一個CPU上,線程A為RR模式,線程B也認為RR模式。兩種優先級一樣。

場景5-相同RR進程,優先級相同,先運行的線程始終占有CPU.jpg

測試結果:先搶占到CPU的線程,始終占有CPU資源。測試結果和RR模式下時間片輪轉,相同優先級會均勻調度的結論不一樣。不知道什么原因?后來在線程中加入適當延時,主動讓出調度機會,得到的結果就符合預期。

場景6:線程中加入適當延時后的

場景5-相同RR進程,優先級相同,先運行的線程始終占有CPU,添加延時.jpg

優先級不同的兩個RR進程,高優先級的RR進程1始終占用CPU,直到進程1退出,進程2才獲取了1次執行機會。

如果兩個線程優先級設置為一致,則平均獲得CPU資源,結果如下圖:
場景5-相同RR進程,優先級相同,先運行的線程始終占有CPU,添加延時-正確.jpg

小結

在多線程程序中,要在應用層提高線程優先級,可通過nice函數提高線程靜態優先級,或者采用SCHED_RR調度模式,同時,采用綁定CPU的方式來獨占某個核,降低cache失效概率,提高線程運行效率。

參考文檔:

  1. linux進程調度之 FIFO 和 RR 調度策略
  2. linux內核普通進程CFS調度原理
  3. linux線程調度策略
  4. 查看代碼運行在哪個CPU
  5. 測試代碼下載鏈接 ,提取碼: dkj3


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM