共計 6639 個字符,預計需要花費 17 分鐘才能閱讀完成。
這篇文章將為大家詳細講解有關 Linux 的進程優先級 NI 和 PR 有哪些區別呢,文章內容質量較高,因此丸趣 TV 小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
為什么要有進程優先級?
這似乎不用過多的解釋,畢竟自從多任務操作系統誕生以來,進程執行占用 cpu 的能力就是一個必須要可以人為控制的事情。因為有的進程相對重要,而有的進程則沒那么重要。
進程優先級起作用的方式從發明以來基本沒有什么變化,無論是只有一個 cpu 的時代,還是多核 cpu 時代,都是通過控制進程占用 cpu 時間的長短來實現的。
就是說在同一個調度周期中,優先級高的進程占用的時間長些,而優先級低的進程占用的短些。
請大家真的不要混淆了系統中的這兩個概念:nice(NI)和 priority(PR),他們有著千絲萬縷的關系,但對于當前的 Linux 系統來說,它們并不是同一個概念。
我們看這個命令:
大家是否真的明白其中 PRI 列和 NI 列的具體含義有什么區別?
同樣的,如果是 top 命令:
大家是否搞清楚了這其中 PR 值和 NI 值的差別? 如果沒有,那么我們可以首先搞清楚什么是 nice 值。
什么是 NICE 值?
NICE 值應該是熟悉 Linux/UNIX 的人很了解的概念了,它是反應一個進程“優先級”狀態的值,其取值范圍是 -20 至 19,一共 40 個級別。
這個值越小,表示進程”優先級”越高,而值越大“優先級”越低。
例如,我們可以通過 NICE 命令來對一個將要執行的 bash 命令進行 NICE 值設置,方法是:
[root@zorrozou-pc0 zorro]# nice -n 10 bash
這樣我就又打開了一個 bash,并且其 nice 值設置為 10,而默認情況下,進程的優先級應該是從父進程繼承來的,這個值一般是 0。
我們可以通過 nice 命令直接查看到當前 shell 的 nice 值:
[root@zorrozou-pc0 zorro]# nice 10
對比一下正常情況:
[root@zorrozou-pc0 zorro]# exit
退出當前 nice 值為 10 的 bash,打開一個正常的 bash,我們查看下其 Nice 值:
[root@zorrozou-pc0 zorro]# bash [root@zorrozou-pc0 zorro]# nice 0
另外,使用 renice 命令可以對一個正在運行的進程進行 nice 值的調整,我們也可以使用比如 top、ps 等命令查看進程的 nice 值,具體方法我就不多說了,大家可以參閱相關 man page。
需要大家注意的是,我在這里都在使用 nice 值這一稱謂,而非優先級 (priority) 這個說法。
nice 值雖然不是 priority,但是它確實可以影響進程的優先級。
在英語中,如果我們形容一個人 nice,那一般說明這個人的人緣比較好。什么樣的人人緣好? 往往是謙讓、有禮貌的人。
比如,你跟一個 nice 的人一起去吃午飯,點了兩個一樣的飯,先上了一份后,nice 的那位一般都會說:“你先吃你先吃!”,這就是人緣好,這人 nice!但是如果另一份上的很晚,那么這位 nice 的人就要餓著了。
這說明什么?
越 nice 的人搶占資源的能力就越差,而越不 nice 的人搶占能力就越強。這就是 nice 值大小的含義,nice 值越低,說明進程越不 nice,搶占 cpu 的能力就越強,優先級就越高(作者這個解釋太形象了,丸趣 TV 小編忍不住要手動點贊!!)。
在原來使用 O1 調度的 Linux 上,我們還會把 nice 值叫做靜態優先級,這也基本符合 nice 值的特點,就是當 nice 值設定好了之后,除非我們用 renice 去改它,否則它是不變的。
而 priority 的值在之前內核的 O1 調度器上表現是會變化的,所以也叫做動態優先級。
什么是優先級和實時進程?
我們再來看看什么是 priority 值,就是 ps 命令中看到的 PRI 值或者 top 命令中看到的 PR 值。
本文為了區分這些概念,以后:
統一用 nice 值表示 NI 值,或者叫做靜態優先級,也就是用 nice 和 renice 命令來調整的優先級;
而實用 priority 值表示 PRI 和 PR 值,或者叫動態優先級。
我們也統一將“優先級”這個詞的概念規定為表示 priority 值的意思。
在內核中,進程優先級的取值范圍是通過一個宏定義的,這個宏的名稱是 MAX_PRIO,它的值為 140。
而這個值又是由另外兩個值相加組成的,一個是代表 nice 值取值范圍的 NICE_WIDTH 宏,另一個是代表實時進程 (realtime) 優先級范圍的 MAX_RT_PRIO 宏。
說白了就是,Linux 實際上實現了 140 個優先級范圍,取值范圍是從 0 -139,這個值越小,優先級越高。nice 值的 -20 到 19,映射到實際的優先級范圍是 100-139。
新產生進程的默認優先級被定義為:
#define DEFAULT_PRIO (MAX_RT_PRIO + NICE_WIDTH / 2)
實際上對應的就是 nice 值的 0。
正常情況下,任何一個進程的優先級都是這個值,即使我們通過 nice 和 renice 命令調整了進程的優先級,它的取值范圍也不會超出 100-139 的范圍,除非這個進程是一個實時進程,那么它的優先級取值才會變成 0 -99 這個范圍中的一個。
這里隱含了一個信息,就是說當前的 Linux 是一種已經支持實時進程的操作系統。
什么是實時操作系統?
我們就不再這里詳細解釋其含義以及在工業領域的應用了,有興趣的可以參考一下實時操作系統的維基百科。
簡單來說,實時操作系統需要保證相關的實時進程在較短的時間內響應,不會有較長的延時,并且要求最小的中斷延時和進程切換延時。
對于這樣的需求,一般的進程調度算法,無論是 O1 還是 CFS 都是無法滿足的,所以內核在設計的時候,將實時進程單獨映射了 100 個優先級,這些優先級都要高于正常進程的優先級(nice 值),而實時進程的調度算法也不同,它們采用更簡單的調度算法來減少調度開銷。
總的來說,Linux 系統中運行的進程可以分成兩類:
實時進程
非實時進程
它們的主要區別就是通過優先級來區分的。
所有優先級值在 0 -99 范圍內的,都是實時進程,所以這個優先級范圍也可以叫做實時進程優先級,而 100-139 范圍內的是非實時進程。
在系統中可以使用 chrt 命令來查看、設置一個進程的實時優先級狀態。我們可以先來看一下 chrt 命令的使用:
我們先來關注顯示出的 Policy options 部分,會發現系統給各種進程提供了 5 種調度策略。
但是這里并沒有說明的是,這五種調度策略是分別給兩種進程用的,對于實時進程可以用的調度策略是:SCHED_FIFO、SCHED_RR,而對于非實時進程則是:SCHED_OTHER、SCHED_OTHER、SCHED_IDLE。
系統的整體優先級策略是:
如果系統中存在需要執行的實時進程,則優先執行實時進程。
直到實時進程退出或者主動讓出 CPU 時,才會調度執行非實時進程。
實時進程可以指定的優先級范圍為 1 -99,將一個要執行的程序以實時方式執行的方法為:
[root@zorrozou-pc0 zorro]# chrt 10 bash [root@zorrozou-pc0 zorro]# chrt -p $$ pid 14840 s current scheduling policy: SCHED_RR pid 14840 s current scheduling priority: 10
可以看到,新打開的 bash 已經是實時進程,默認調度策略為 SCHED_RR,優先級為 10。如果想修改調度策略,就加個參數:
[root@zorrozou-pc0 zorro]# chrt -f 10 bash [root@zorrozou-pc0 zorro]# chrt -p $$ pid 14843 s current scheduling policy: SCHED_FIFO pid 14843 s current scheduling priority: 10
剛才說過,SCHED_RR 和 SCHED_FIFO 都是實時調度策略,只能給實時進程設置。對于所有實時進程來說,優先級高的 (就是 priority 數字小的) 進程一定會保證先于優先級低的進程執行。
SCHED_RR 和 SCHED_FIFO 的調度策略只有當兩個實時進程的優先級一樣的時候才會發生作用,其區別也是顧名思義:
SCHED_FIFO
以先進先出的隊列方式進行調度,在優先級一樣的情況下,誰先執行的就先調度誰,除非它退出或者主動釋放 CPU。
SCHED_RR
以時間片輪轉的方式對相同優先級的多個進程進行處理。時間片長度為 100ms。
這就是 Linux 對于實時進程的優先級和相關調度算法的描述。整體很簡單,也很實用。
而相對更麻煩的是非實時進程,它們才是 Linux 上進程的主要分類。對于非實時進程優先級的處理,我們首先還是要來介紹一下它們相關的調度算法:O1 和 CFS。
什么是 O1 調度?
O1 調度算法是在 Linux 2.6 開始引入的,到 Linux 2.6.23 之后內核將調度算法替換成了 CFS。
雖然 O1 算法已經不是當前內核所默認使用的調度算法了,但是由于大量線上的服務器可能使用的 Linux 版本還是老版本,所以我相信很多服務器還是在使用著 O1 調度器,那么費一點口舌簡單交代一下這個調度器也是有意義的。
這個調度器的名字之所以叫做 O1,主要是因為其算法的時間復雜度是 O1。
O1 調度器仍然是根據經典的時間片分配的思路來進行整體設計的。
簡單來說,時間片的思路就是將 CPU 的執行時間分成一小段一小段的,假如是 5ms 一段。于是多個進程如果要“同時”執行,實際上就是每個進程輪流占用 5ms 的 cpu 時間,而從 1s 的時間尺度上看,這些進程就是在“同時”執行的。
當然,對于多核系統來說,就是把每個核心都這樣做就行了。而在這種情況下,如何支持優先級呢?
實際上就是將時間片分配成大小不等的若干種,優先級高的進程使用大的時間片,優先級小的進程使用小的時間片。這樣在一個周期結速后,優先級大的進程就會占用更多的時間而因此得到特殊待遇。
O1 算法還有一個比較特殊的地方是,即使是相同的 nice 值的進程,也會再根據其 CPU 的占用情況將其分成兩種類型:CPU 消耗型和 IO 消耗性。
典型的 CPU 消耗型的進程的特點是,它總是要一直占用 CPU 進行運算,分給它的時間片總是會被耗盡之后,程序才可能發生調度。
比如常見的各種算數運算程序。
而 IO 消耗型的特點是,它經常時間片沒有耗盡就自己主動先釋放 CPU 了。
比如 vi,emacs 這樣的編輯器就是典型的 IO 消耗型進程。
為什么要這樣區分呢? 因為 IO 消耗型的進程經常是跟人交互的進程,比如 shell、編輯器等。
當系統中既有這種進程,又有 CPU 消耗型進程存在,并且其 nice 值一樣時,假設給它們分的時間片長度是一樣的,都是 500ms,那么人的操作可能會因為 CPU 消耗型的進程一直占用 CPU 而變的卡頓。
可以想象,當 bash 在等待人輸入的時候,是不占 CPU 的,此時 CPU 消耗的程序會一直運算,假設每次都分到 500ms 的時間片,此時人在 bash 上敲入一個字符的時候,那么 bash 很可能要等個幾百 ms 才能給出響應,因為在人敲入字符的時候,別的進程的時間片很可能并沒有耗盡,所以系統不會調度 bash 程度進行處理。
為了提高 IO 消耗型進程的響應速度,系統將區分這兩類進程,并動態調整 CPU 消耗的進程將其優先級降低,而 IO 消耗型的將其優先級變高,以降低 CPU 消耗進程的時間片的實際長度。
已知 nice 值的范圍是 -20-19,其對應 priority 值的范圍是 100-139,對于一個默認 nice 值為 0 的進程來說,其初始 priority 值應該是 120,隨著其不斷執行,內核會觀察進程的 CPU 消耗狀態,并動態調整 priority 值,可調整的范圍是 +-5。
就是說,*** 優先級可以被自動調整到 115,*** 到 125。這也是為什么 nice 值叫做靜態優先級,而 priority 值叫做動態優先級的原因。不過這個動態調整的功能在調度器換成 CFS 之后就不需要了,因為 CFS 換了另外一種 CPU 時間分配方式,這個我們后面再說。
什么是 CFS 完全公平調度?
O1 已經是上一代調度器了,由于其對多核、多 CPU 系統的支持性能并不好,并且內核功能上要加入 cgroup 等因素,Linux 在 2.6.23 之后開始啟用 CFS 作為對一般優先級 (SCHED_OTHER) 進程調度方法。
在這個重新設計的調度器中,時間片,動態、靜態優先級以及 IO 消耗,CPU 消耗的概念都不再重要。CFS 采用了一種全新的方式,對上述功能進行了比較完善的支持。
其設計的基本思路是:我們想要實現一個對所有進程完全公平的調度器。
又是那個老問題:如何做到完全公平? 答案跟上一篇 IO 調度中 CFQ 的思路類似:
如果當前有 n 個進程需要調度執行,那么調度器應該在一個比較小的時間范圍內,把這 n 個進程全都調度執行一遍,并且它們平分 cpu 時間,這樣就可以做到所有進程的公平調度。
那么這個比較小的時間就是任意一個 R 狀態進程被調度的 *** 延時時間,即:任意一個 R 狀態進程,都一定會在這個時間范圍內被調度響應。這個時間也可以叫做調度周期,其英文名字叫做:sched_latency_ns。
CFS 的優先級
當然,CFS 中還需要支持優先級。在新的體系中,優先級是以時間消耗 (vruntime 增長) 的快慢來決定的。
就是說,對于 CFS 來說,衡量的時間累積的絕對值都是一樣紀錄在 vruntime 中的,但是不同優先級的進程時間增長的比率是不同的,高優先級進程時間增長的慢,低優先級時間增長的快。
比如,優先級為 19 的進程,實際占用 cpu 為 1 秒,那么在 vruntime 中就記錄 1s。但是如果是 -20 優先級的進程,那么它很可能實際占 CPU 用 10s,在 vruntime 中才會紀錄 1s。
CFS 真實實現的不同 nice 值的 cpu 消耗時間比例在內核中是按照“每差一級 cpu 占用時間差 10% 左右”這個原則來設定的。
這里的大概意思是說,如果有兩個 nice 值為 0 的進程同時占用 cpu,那么它們應該每人占 50% 的 cpu,如果將其中一個進程的 nice 值調整為 1 的話,那么此時應保證優先級高的進程比低的多占用 10% 的 cpu,就是 nice 值為 0 的占 55%,nice 值為 1 的占 45%。那么它們占用 cpu 時間的比例為 55:45。
這個值的比例約為 1.25。就是說,相鄰的兩個 nice 值之間的 cpu 占用時間比例的差別應該大約為 1.25。根據這個原則,內核對 40 個 nice 值做了時間計算比例的對應關系,它在內核中以一個數組存在:
多 CPU 的 CFS 調度是怎樣的?
在上面的敘述中,我們可以認為系統中只有一個 CPU,那么相關的調度隊列只有一個。
實際情況是系統是有多核甚至多個 CPU 的,CFS 從一開始就考慮了這種情況,它對每個 CPU 核心都維護一個調度隊列,這樣每個 CPU 都對自己的隊列進程調度即可。
這也是 CFS 比 O1 調度算法更高效的根本原因:每個 CPU 一個隊列,就可以避免對全局隊列使用大內核鎖,從而提高了并行效率。
當然,這樣最直接的影響就是 CPU 之間的負載可能不均,為了維持 CPU 之間的負載均衡,CFS 要定期對所有 CPU 進行 load balance 操作,于是就有可能發生進程在不同 CPU 的調度隊列上切換的行為。
這種操作的過程也需要對相關的 CPU 隊列進行鎖操作,從而降低了多個運行隊列帶來的并行性。
不過總的來說,CFS 的并行隊列方式還是要比 O1 的全局隊列方式要高效。尤其是在 CPU 核心越來越多的情況下,全局鎖的效率下降顯著增加。
丸趣 TV 小編的目的是從 Linux 系統進程的優先級為出發點,我們也對 CFS 調度算法進行了比較深入的分析。在我的經驗來看,這些知識對我們在觀察系統的狀態和相關優化的時候都是非常有用的。
比如在使用 top 命令的時候,NI 和 PR 值到底是什么意思? 類似的地方還有 ps 命令中的 NI 和 PRI 值、ulimit 命令 - e 和 - r 參數的區別等等。當然,希望看完本文后,能讓大家對這些命令顯示的了解更加深入。
除此之外,我們還會發現,雖然 top 命令中的 PR 值和 ps - l 命令中的 PRI 值的含義是一樣的,但是在優先級相同的情況下,它們顯示的值確不一樣。
關于 Linux 的進程優先級 NI 和 PR 有哪些區別呢就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。