linux線程調度策略
這是一篇非常好的關於線程調度的資料,翻譯自shed
- linux線程調度策略
- Scheduling policies
- Resetting scheduling policy for child processes
- Privileges and resource limits
- Limiting the CPU usage of real-time and deadline processes(限制實時進程或deadline進程)
- Response time
- Miscellaneous
- The autogroup feature
- Real-time features in the mainline Linux kernel
- TIPS:
從Linux 2.6.23開始,默認的調度器為CFS,即"完全公平調度器"(Completely Fair Scheduler)。CFS調度器取代了之前的"O(1)"調度器。
CFS的實現細節可以參見sched-design-CFS。cgroup的CPU調度也屬於CFS擴展的一部分。
Scheduling policies
內核模塊使用調度器來決定下一個CPU時鍾周期執行的線程。每個線程都包含一個調度策略以及一個靜態的調度優先級sched_priority
,調度器根據系統上所有線程的調度策略和靜態優先級來決定如何進行調度。
對於使用普通調度策略(SCHED_OTHER
, SCHED_IDLE
, SCHED_BATCH
)的線程來說,sched_priority
並不會影響調度結果,且必須設置為0。
對於使用實時策略(SCHED_FIFO
,SCHED_RR
)的進程,其sched_priority
取值為1到99(1為最低值)。實時線程的調度優先級總是高於普通線程。注:POSIX.1的系統在實現中,會要求實時調度策略有32個優先級設置,因此,為了可移植性,可以使用sched_get_priority_min和sched_get_priority_max來查找調度策略所支持的優先級范圍。
調度器會為每個sched_priority
值維護一個可運行的線程列表。調度器通過查看非空且靜態優先級最高的列表,並選擇該列表首部的元素作為下一個運行的線程。
線程的調度策略決定了如何根據靜態優先級來將一個線程插入到同靜態優先級的線程列表(list of runnable threads)中,以及如何在該列表中調整線程的位置。
所有的調度都具有搶占性:如果一個具有更高靜態優先級的線程准備運行,當前運行的線程會被搶占並返回到其靜態優先級對應的等待列表中。調度策略僅根據具有相同靜態優先級的可運行線程列表來決定調度順序。
進程調度中使用了2個隊列:進程一開始會進入ready隊列等待調度;當進程執行中遇到I/O阻塞,等待子進程結束或軟中斷等原因會進入wait隊列,等阻塞結束后會返回到ready隊列
SCHED_FIFO: First in-first out scheduling(實時線程)
SCHED_FIFO
僅適用於靜態優先級大於0的線程,即當一個SCHED_FIFO
的線程變為可運行(runnable)狀態時,它會立即搶占所有當前運行的SCHED_OTHER
, SCHED_BATCH
或SCHED_IDLE
線程。SCHED_FIFO
不使用時間片進行調度,所有使用SCHED_FIFO
調度策略的線程應該遵守如下規則:
-
當一個運行中的
SCHED_FIFO
線程被其他有更高優先級的線程搶占后,該線程會返回到其優先級對應的列表的首部,當所有更高優先級的線程阻塞后,該線程將會立即恢復運行; -
當一個阻塞的
SCHED_FIFO
線程變為可運行狀態時,該線程會返回到其優先級對應的列表末尾; -
如果調用 sched_setscheduler(2),sched_setparam(2),sched_setattr(2),pthread_setschedparam(3),pthread_setschedprio(3) (通過pid)修改了正在運行或可運行狀態的
SCHED_FIFO
線程的優先級時,該線程在列表中的位置取決於優先級的變動:-
如果線程優先級增加了,它將會放置到新優先級對應的列表末尾,同時可能搶占正在運行的具有相同優先級的線程;
-
如果線程優先級沒變,其在運行列表中的位置不變;
-
如果線程優先級減小了,它將會放置到新優先級對應的列表的前面。
根據POSIX.1-2008,通過非 pthread_setschedprio(3)方式來修改線程的優先級,可能會導致其放置到對應優先級列表的末尾。
-
-
調用了sched_yield(2) (用於釋放CPU)的線程將會放置到列表末尾
SCHED_FIFO
線程將會一直運行,直到被更高優先級的線程搶占,或調用了sched_yield(2) 。
SCHED_RR: Round-robin scheduling(輪詢調度)
SCHED_RR
對SCHED_FIFO
做了簡單增強。除每個線程僅允許運行在一個最大時間段下外,SCHED_FIFO
中的所有規則都適用於SCHED_RR
。如果一個SCHED_RR
線程已經運行了等於或大於該最大時間段時,該線程會被放置到其優先級列表的末尾。當一個SCHED_RR
線程被更高優先級的線程搶占,並在后續恢復運行后,會在先前未過期的時間段下運行。最大時間段可以通過sched_rr_get_interval(2)獲得。
SCHED_DEADLINE: Sporadic task model deadline scheduling
3.14版本之后的Linux提供了一個新的調度策略SCHED_DEADLINE
。該策略結合了GEDF(Global Earliest Deadline First)和 CBS (Constant Bandwidth Server)。必須通sched_setattr(2)和sched_getattr(2)來設置和獲取該策略。
一個Sporadic task被定義為一系列任務,且每個任務每次僅激活一次。每個任務都有一個relative deadline
(該任務應該在該相對時間前停止運行),以及一個computation time
(執行該任務需要的CPU時間,對應下圖的comp. time
)。一個新的任務開始執行時會喚醒(wakeup)一個Sporadic task,該時間點被稱為arrival time
,start time
為一個任務開始執行的時間,absolute deadline
(絕對截止時間)為arrival time
加上relative deadline
的時間點。
arrival/wakeup absolute deadline
| start time |
| | |
v v v
-----x--------xooooooooooooooooo--------x--------x---
|<- comp. time ->|
|<------- relative deadline ------>|
|<-------------- period ------------------->|
當使用sched_setattr(2)給一個線程設置SCHED_DEADLINE
策略時,可以設置3個參數:Runtime
, Dead‐line
和Period
,對於上面提到的場景來說,通常的做法是將Runtime
設置為大於平均計算時間的值(或更壞的場景下,設置為硬實時任務的執行時間);將Deadline
設置為對應的dead-line,將Period
設置為任務的周期,此時對於SCHED_DEADLINE
的調度如下:
Runtime
對應上圖中的comp.time
,Dead-line
對應上圖的relative deadline
arrival/wakeup absolute deadline
| start time |
| | |
v v v
-----x--------xooooooooooooooooo--------x--------x---
|<-- Runtime ------->|
|<----------- Deadline ----------->|
|<-------------- Period ------------------->|
3個deadline調度參數對應sched_attr
結構體中的sched_run‐time
, sched_deadline
, 和sched_period
字段,參見sched_setattr(2)。這些字段的單位為納秒。如果sched_period
的值為0,則它與sched_deadline
相同。
內核要求:
sched_runtime <= sched_deadline <= sched_period
此外,在當前實現中,所有參數的值至少為1024(即,大於1微秒),小於2^63。如果有效性校驗失敗,sched_setattr(2)返回EINVAL錯誤。
CBS通過阻止線程超出其運行時間Runtime
來保證任務間不互相干擾。
為了確保deadline調度,當SCHED_DEADLINE
線程在給定的條件下不可運行時,此時內核必須阻止這些線程的運行。內核必須在設置或修改SCHED_DEADLINE
策略和屬性時執行准入測試。准入測試用於計算這些修改是否可行,如果不可行,sched_setattr(2)將返回EBUSY錯誤。
例如,總的CPU利用率應該小於或等於總的可用的CPU。由於每個線程可以在每個Period
中最大運行Runtime
時間,線程的CPU時間片使用率為Runtime
除以Period
。
為了滿足SCHED_DEADLINE
的條件,使用SCHED_DEADLINE策略的線程的優先級是系統中最高的。當一個SCHED_DEADLINE
線程運行時,該線程會搶占其他策略下調度的線程。
對SCHED_DEADLINE
策略調度的線程調用fork(2)會返回EAGAIN錯誤(除非該線程設置了reset-on-fork標記)。
當一個SCHED_DEADLINE
線程調用了sched_yield(2)將會停止當前任務,並等待新的周期。
SCHED_OTHER: Default Linux time-sharing scheduling(默認策略)
SCHED_OTHER
只能在靜態優先級為0時使用(普通線程)。SCHED_OTHER
是標准的Linux分時調度策略(不需要實時機制)。
如何從靜態優先級為0的列表中選擇運行的線程取決於列表中的dynamic
優先級。dynamic
優先級基於nice值,且在每次線程准備運行時增加。這種機制保證公平處理所有的SCHED_OTHER
線程。
在Linux內核源碼中,SCHED_OTHER
被稱為SCHED_NORMAL
。
The nice value
nice值用於影響CPU調度器對進程的調度偏好。適用於SCHED_OTHER和SCHED_BATCH調度處理。可以通過nice(2),setpriority(2)或sched_setattr(2)修改nice值。
根據POSIX.1,nice值是一個單進程屬性,即進程中的所有線程共享一個nice值。然而,在Linux中,nice值是一個單線程屬性,相同進程中的不同線程可能使用不同的nice值。
nice值的取值范圍根據UNIX系統的不同而不同。在現代Linux系統中,取值為-20(高優先級)到+19(低優先級),而一些系統中的取值為-20..20。在一些非常早期的Linux 內核(Linux 2.0之前)中的取值為-infinity..15。
nice值對相應的SCHED_OTHER
進程的影響根據UNIX系統和Linux內核版本的不同而不同。
2.6.23版本的Linux內核中引入了CFS調度,並采用了一種能根據nice的差值產生更顯著影響的算法。在當前的實現下,兩個進程的nice差值中,每單位的nice差值對CFS調度的影響因子為1.25 (參見how-is-nice-working,CFS根據vruntime進行CPU調度:vruntime = 實際運行時間 * 1024 / 進程權重
,進程權重
為1.25 ^ nice_value
)。這種算法使得在有高優先級負載運行的情況下,只能給低nice值(+19)的負載提供很小的CPU;而為高nice值(-20)的負載提供其運行應用需要的絕大部分CPU(如音頻應用)。
權重表示是該程序需要的cpu時間的一種表現,如果一個程序需要大量cpu進行處理,可以提高其權重,反之減小其權重。CFS的思想就是讓每個調度實體的vruntime互相追趕,而每個線程的vruntime增加速度不同,權重越大的增加的越慢,這樣就能獲得更多的cpu執行時間。系統將會根據每個線程的vruntime排序(實際上是基於紅黑樹算法),vruntime最小的線程會最早獲得調度。而一旦vruntime的次序發生變化(vruntime的大小與實際運行時間有關,運行時間越長,其值越大),系統將嘗試觸發下一次調度。也就是說調度器盡可能的保證所有線程的vruntime都一致,權重高的線程vruntime提升的慢(進程權重小,即分子小),容易被優先調度;權重低,同樣的時間上vruntime上升的快,反而容易被輪空。一個線程的vruntime可以通過
/proc/<PID>/sched
中的se.vruntime
選項查看:grep vruntime /proc/<PID>/sched
。vruntime的增加與進程占用的CPU有關,如果一個線程一直處於sleep狀態,其vruntime是不會增加的。那么如果一個sleep的線程被喚醒之后,是否會立即搶占vruntime比它大的線程?答案是否定的,其最少需要在cpu的run隊列中等待sched_min_granularity_ns的時間,sched_min_granularity_ns的計算方式如下:If number of runnable tasks does not exceed sched_latency_ns/sched_min_granularity_ns scheduler period = sched_latency_ns else scheduler period = number_of_running_tasks * sched_min_granularity_ns
可以使用
top
命令查看系統上的nice值和優先級。如下PR
表示優先級,NI
表示nice值,前者為內核角度看的進程的實際優先級,后者為用戶空間看到的進程的nice值。兩者的關系為:PR = 20 + NI
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 1145 root 20 0 602236 60888 25096 S 0.7 1.2 0:09.70 containerd 61 root 20 0 0 0 0 I 0.3 0.0 0:02.91 kworker/1:1-eve 1 root 20 0 193736 8288 5644 S 0.0 0.2 0:01.15 systemd 2 root 20 0 0 0 0 S 0.0 0.0 0:00.01 kthreadd 3 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 rcu_gp 4 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 rcu_par_gp 5 root 20 0 0 0 0 I 0.0 0.0 0:00.23 kworker/0:0-ata 6 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 kworker/0:0H-ev 7 root 20 0 0 0 0 I 0.0 0.0 0:00.17 kworker/u256:0- 8 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 mm_percpu_wq 9 root 20 0 0 0 0 S 0.0 0.0 0:00.00 ksoftirqd/0 10 root 20 0 0 0 0 I 0.0 0.0 0:00.42 rcu_sched 11 root rt 0 0 0 0 S 0.0 0.0 0:00.00 migration/0 ...
Linux系統可以使用RLIMIT_NICE資源來限制非特權進程的nice值的上限,參見setrlimit(2)。
更多nice值的用法,參見下面的autogroup。
SCHED_BATCH: Scheduling batch processes
從Linux 2.6.16開始,SCHED_BATCH
可以用於靜態優先級為0的線程。該策略類似SCHED_OTHER
,並根據動態優先級(nice值)進行調度。區別是使用該策略時,調度器會假設線程是CPU密集型的,因此,該調度器會根據線程的喚醒行為施加調度懲罰,因此這種調度策略比較不受歡迎。
該策略比較適用於非交互且不期望降低nice值的負載,以及需要不因為交互而(在負載之間)造成額外搶占的調度策略的負載。下面引用自PHP-FPM on Linux, SCHED_BATCH or SCHED_OTHER?,更多參見[batch/idle priority scheduling, SCHED_BATCH](batch/idle priority scheduling, SCHED_BATCH)
SCHED_BATCH was clearly designed for very long running (hours or even days) compute-intensive jobs. Your jobs are only compute-intensive for seconds or fractions of seconds.
This pretty much makes it a no-go for a web server. And it would be worse if the database is on the same machine, as they might contend for one of those extra-long timeslices.
SCHED_IDLE: Scheduling very low priority jobs
從Linux 2.6.23開始,SCHED_IDLE
可以用於靜態優先級為0的線程。nice值不會影響該策略。
該策略用於運行非常低優先級的任務(低於nice值為+19的SCHED_OTHER
或SCHED_BATCH
策略)。
Resetting scheduling policy for child processes
每個線程都有一個reset-on-fork調度標識。當設置該標識后,使用fork(2)創建的子進程不會繼承特權調度策略。可以通過如下方式設置reset-on-fork:
- 在調用sched_setscheduler(2)時,將SCHED_RESET_ON_FORK 標識作為
policy
參數,或 - 在調用sched_setattr(2)時,將SCHED_FLAG_RESET_ON_FORK 設置為
attr.sched_flags
注意上面兩個函數的常量名稱不一樣。使用sched_getscheduler(2)和sched_getattr(2)獲取reset-on-fork狀態的用法與上面類似。
reset-on-fork特性用於媒體播放的應用,可以防止應用在創建多個子進程時規避RLIMIT_RTTIME設置的資源限制。
更精確地講,如果設置了reset-on-fork,后續創建地子進程會遵循下面規則:
- 如果正在運行的線程使用了
SCHED_FIFO
或SCHED_RR
調度策略,子進程地策略或被設置為SCHED_OTHER
; - 如果正在運行的進程的nice值為負值,子進程的nice值會被設置為0。
在設置reset-on-fork之后,只有線程擁有CAP_SYS_NICE的capability時才能重置reset-on-fork。使用fork(2)創建的子進程會disable reset-on-fork標識。
Privileges and resource limits
在Linux 2.6.12之前,只有擁有特權(CAP_SYS_NICE)的線程才能設置非0的靜態優先級(即設置實時調度策略)。后續版本對如下實現進行了修改:非特權的線程僅在調用者的effective user ID(EID)與目標線程的real或effective user ID相同的情況下才能且僅能設置SCHED_OTHER策略。
為了設置或修改SCHED_DEADLINE
策略。線程必須是特權(CAP_SYS_NICE)的。
從Linux 2.6.12開始,RLIMIT_RTPRIO(可以使用ulimit -e設置)資源限制定義了非特權線程設置SCHED_RR 和SCHED_FIFIO策略的靜態優先級的上限。修改調度策略和優先級的規則如下:
- 如果非特權線程有一個非0的RLIMIT_RTPRIO 軟限制(soft limit),則該線程對調度策略和優先級的修改限制為:優先級的不能高於當前優先級且不能高於RLIMIT_RTPRIO。
- 如果RLIMIT_RTPRIO 為0,則僅允許降低優先級,或切換到非實時策略。
- 遵從上述規則的前提下,只要執行修改的線程的effective user ID等於目標線程的effective user ID就可以執行相應的修改。
- SCHED_IDLE策略有特殊的約束。在Linux 2.6.39之前,在該策略下創建的非特權線程無法修改該策略(與RLIMIT_RTPRIO 資源限制無關)。從Linux 2.6.39開始,只要nice值在RLIMIT_RTPRIO 資源限制所允許的范圍內,非特權線程可以切換到SCHED_BATCH或SCHED_OTHER策略。
特權(CAP_SYS_NICE)線程會忽略RLIMIT_RTPRIO限制。在一些老的內核中,特權線程可以任意修改策略和優先級。參見 getrlimit(2)獲取更多信息。
Limiting the CPU usage of real-time and deadline processes(限制實時進程或deadline進程)
SCHED_FIFO, SCHED_RR或SCHED_DEADLINE策略下調度的線程中的非阻塞無限循環處理可能會阻塞其他線程獲取CPU。在Linux 2.6.25之前,阻止實時進程凍結系統的唯一方式是通過shell啟動一個靜態優先級更高的程序,如通過這種方式來停止實施程序,並釋放CPU資源。
從Linux 2.6.25開始,引進了其他技術手段來處理實時(SCHED_FIFO,SCHED_RR)和deadline(SCHED_DEADLINE)進程。一種方式是通過RLIMIT_RTTIME 來限制實時進程可能使用到的CPU的上限。參見 getrlimit(2)獲取更多信息。
從Linux 2.6.25開始,Linux提供了2個/proc
文件來為非實時進程保留CPU時間。保留的CPU也可以為shell預留資源來停止正在允許的進程。兩個文件中的值對應的單位為微秒:
-
/proc/sys/kernel/sched_rt_period_us
該文件中的值指定了等同於100% CPU的調度周期。取值范圍為1到INT_MAX,即1微秒到35分鍾。默認值為1000,000(1秒)。定義了一個CPU使用周期,周期越短,可以越快開始下一個周期
-
/proc/sys/kernel/sched_rt_runtime_us
該文件中的值指定了實時和deadline調度的進程可以使用的"period"。取值范圍為-1到INT_MAX-1,設置為-1標識運行時間等同於周期,即沒有給非實時進程預留任何CPU。默認值為950,000(0.95秒),表示給非實時或deadline調度策略保留5%的CPU。該參數需要結合
sched_rt_period_us
使用
Response time
一個阻塞的高優先級的線程(在調度前)等待I/O時會有一個確定的響應時間。設備驅動作者可以使用"slow interrupt"中斷句柄來減少響應時間
Miscellaneous
子進程會通過fork(2)繼承調度策略和參數。可以使用execve(2)來保存調度策略和參數。
實時進程通常會使用memory locking特性來防止內存頁的延遲。可以使用mlock(2) 或mlockall(2)設置memory locking。
The autogroup feature
從Linux 2.6.38開始,內核提供了一種被稱為autogrouping的特性來為多進程和CPU密集型負載(如Linux內核中的大量並行進程)提升交互式桌面性能。
該特性結合CFS調度策略,需要內核設置CONFIG_SCHED_AUTOGROUP
。在一個運行的系統中,該特性可以通過文件/proc/sys/kernel/sched_autogroup_enabled
使能或去使能,值0表示去使能,1表示使能。默認值為1(除非內核使用noautogroup
參數啟動內核)。
當通過setsid(2) (setsid會將一個進程脫離父進程)創建一個新的會話時會創建一個新的autogroup,這種情況可能發生在一個新的終端窗口啟動時。使用fork(2)創建的進程會繼承父輩的autogroup成員。因此,一個會話中的所有進程都屬於同一個autogroup。當最后一個進程結束后,autogroup會被自動銷毀。
當使能autogrouping時,一個autogroup中的所有成員都屬於同一個內核調度器"任務組"。CFS調度器使用了在任務組間均衡分配CPU時鍾周期的算法。可以使用下面例子進行展示提升交互式桌面性能的好處。
假設有2個競爭相同CPU的autogroup(即,單核系統或使用taskset設置所有SMP系統的進程使用相同的CPU),第一個group包含10個用於構建內核的CPU密集型進程make -j10CPU
;另外一個包含一個CPU密集型的視頻播放器進程。autogrouping的影響為:每個group各自分配到一半的CPU時鍾周期,即視頻播放器會分配到50%的CPU時鍾周期,而非9%的時鍾周期(該情況下可能會導致降低視頻播放質量)。在SMP系統上會更加復雜,但整體的表現是一樣的:調度器會在任務組之間分配CPU時鍾周期,包含大量CPU密集型進程的autogroup並不會以犧牲系統上的其他任務為代價占用CPU周期。
進程的autogroup成員可以通過/proc/[pid]/autogroup
查看:(下面進程隸屬於autogroup-1,autogroup-1的nice值為0)
$ cat /proc/1/autogroup
/autogroup-1 nice 0
該文件可以通過為autogroup設置nice
值來修改分配給一個autogroup的CPU帶寬(bandwidth,即可使用的CPU時間),nice值范圍為+19(低優先級)到-20(高優先級),設置越界的值會導致write(2)返回EINVAL錯誤。
autogroup的nice值的意義與進程的nice值意義相同,區別是前者為將autogroup作為一個整體,並基於相對其他autogroups設置的nice值來分配CPU時鍾周期。對於一個autogroup內的進程,其CPU時鍾周期為autogroup(相對於其他autogroups)的nice值和進程的nice值(相對於其他進程)的產物(即首先根據autogroup的nice值計算該autogroup所占用的CPU,然后根據進程的nice值計算該進程所占用的(屬於其autogroup的)CPU)。
可以使用cgroups(7) CPU控制器來設置(非root CPU cgroup的)cgroups中的進程所占用的CPU,該設置會覆蓋掉autogrouping。
所有的cgroup都由可選擇的內核配置CONFIG_CGROUPS控制。在Linux 3.2引入了CPU帶寬控制(bandwidth control)。cgroup對CFS的擴展有如下三種:
CONFIG_CGROUP_SCHED :運行任務以組的方式(在組之間)公平使用CPU
CONFIG_RT_GROUP_SCHED :用於支持實時任務組(SCHED_FIFO和SCHED_RR)
CONFIG_FAIR_GROUP_SCHED :用於支持CFS任務組(SCHED_NORMAL和SCHED_BATCH)
可以用如下方式查看是否啟用了cgroup
# zgrep -i cgroup /boot/config-3.10.0-693.el7.x86_64
CONFIG_CGROUPS=y
# CONFIG_CGROUP_DEBUG is not set
CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_PIDS=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_CGROUP_HUGETLB=y
CONFIG_CGROUP_PERF=y
CONFIG_CGROUP_SCHED=y
CONFIG_BLK_CGROUP=y
...
autogroup特性僅用於非實時調度策略(SCHED_OTHER, SCHED_BATCH和SCHED_IDLE)。它不會為實時和deadline策略分組。
The nice value and group scheduling
當調度非實時進程時,CFS調度器會使用一種稱為"group scheduling"的技術(如果內核設置了CONFIG_FAIR_GROUP_SCHED
選項)
在group scheduling下,進程以"任務組"方式進行調度。任務組間有繼承關系,會繼承系統上被稱為"root任務組"的初始化任務組。任務組遵循以下條件(按順序):
- CPU cgroup中的所有線程為一個任務組。該任務組的父輩為對應的父cgroup
- 如果使能了autogrouping,則一個autogroup(即,使用setsid(2)創建的相同的會話)中的所有線程為一個任務組。每個新的autogrouping為獨立的任務組。root任務組為所有任務組的父輩。
- 如果使能了autogrouping,那么包含所有root CPU cgroup進程的root任務組不會(隱式地)放到一個新的autogroup中。
- 如果使能了autogrouping,那么root任務組包含所有root CPU croup中的進程。
- 如果去使能autogrouping(即內核不配置CONFIG_FAIR_GROUP_SCHED),那么系統中所有的進程都會被放到一個任務組中
在group調度下,線程的nice值僅會影響到相同任務組的其他線程的調度。這會在一些使用傳統nice語義的UNIX系統上會導致驚人的后果。實踐中,如果使能了autogrouping,則會使用setpriority(2)或nice(1)來影響相同會話(通常為相同的終端窗口)中的一個進程相對於其他進程的調度。
相反的,對於不同會話(如,不同的終端窗口,這些任務都綁定到不同的autogroups)中綁定了唯一的CPU的2個進程,修改一個會話中的進程的nice值不會影響其他會話中的進程的調度。使用如下命令可以修改一個終端會話中所有進程對於的autogroup nice值。
$ echo 10 > /proc/self/autogroup
autogroup和進程都有一個nice值,autogroup的nice值用於在autogroup之間分配CPU;autogroup內的進程的nice值用於在進程間分配autogroup的CPU。cgroup的配置會覆蓋autogroup
Real-time features in the mainline Linux kernel
從Linux 2.6.18開始,Linux逐漸具備實時功能,其中大部分來源於realtime-preempt
補丁集。在這些補丁最終合並到內核主線之前,它們必須通過安裝才能達到實時性能。這些補丁命名為:
patch-kernelversion-rtpatchversion
可以從這里下載。
如果沒有補丁且在這些補丁完全合並到主線之前的內核提供了3中搶占類,CONFIG_PREEMPT_NONE,CONFIG_PREEMPT_VOLUNTARY和CONFIG_PREEMPT_DESKTOP,分別表示沒有,部分,和考慮降低最壞情況下的調度延遲。
如果沒有補丁且在這些補丁完全合並到主線之前的內核還提供了額外的配置表項CONFIG_PREEMPT_RT,如果選擇該表項,Linux會轉變為正式的實時操作系統。FIFO和RR調度策略會用於運行具有實時優先級且最小調度延遲的線程。
TIPS:
-
使用
ps -eLfc
可以在CLS
一欄中查看進程的調度策略,最下面為最新內核中定義的調度策略(5.5)TS SCHED_OTHER FF SCHED_FIFO RR SCHED_RR B SCHED_BATCH ISO SCHED_ISO IDL SCHED_IDLE
/* * Scheduling policies */ #define SCHED_NORMAL 0 #define SCHED_FIFO 1 #define SCHED_RR 2 #define SCHED_BATCH 3 /* SCHED_ISO: reserved but not implemented yet */ #define SCHED_IDLE 5 #define SCHED_DEADLINE 6
-
可以在/proc/sched_debug中查看各個cpu core的調度情況,其中包含每個cgroup 服務的調度情況,如下面為docker服務在core1上的調度情況
cfs_rq[1]:/system.slice/docker.service .exec_clock : 0.000000 .MIN_vruntime : 0.000001 .min_vruntime : 2723538.752185 .max_vruntime : 0.000001 .spread : 0.000000 .spread0 : -780278343.308552 .nr_spread_over : 0 .nr_running : 0 .load : 0 .runnable_load_avg : 0 .blocked_load_avg : 1 .tg_load_avg : 1 .tg_load_contrib : 1 .tg_runnable_contrib : 3 .tg->runnable_avg : 8 .tg->cfs_bandwidth.timer_active: 0 .throttled : 0 .throttle_count : 0 .se->exec_start : 7432565479.124290 .se->vruntime : 560032308.234830 .se->sum_exec_runtime : 7762399.141979 .se->load.weight : 2 .se->avg.runnable_avg_sum : 147 .se->avg.runnable_avg_period : 47729 .se->avg.load_avg_contrib : 1 .se->avg.decay_count : 7088246803
可以在/proc/$pid/sched中查看特定進程的調度情況
# cat sched docker-proxy-cu (77992, #threads: 8) ------------------------------------------------------------------- se.exec_start : 7179182946.125343 se.vruntime : 1843988.364695 se.sum_exec_runtime : 6.017643 se.nr_migrations : 2 nr_switches : 6 nr_voluntary_switches : 4 nr_involuntary_switches : 2 se.load.weight : 1024 policy : 0 prio : 120 clock-delta : 34 mm->numa_scan_seq : 0 numa_migrations, 0 numa_faults_memory, 0, 0, 1, 0, -1 numa_faults_memory, 1, 0, 0, 0, -1
-
一個線程可以通過系統分配的時間片在各個CPU core上允許,但一個線程不能同時在多個core上運行。如果一個系統有N個core,那么可以同時在這些core上允許N個線程。將CPU core上線程切換到另外一個線程時,會涉及到上下文切換,上下文切換時需要保存PCB中的CPU寄存器信息,進程狀態以及內存管理等信息,在進程恢復時還原這些信息。
-
可以通過/proc/$pid/status查看進程上下文切換的情況,如下表示自發(如I/O等待)的上下文切換為1,非自發(如時間片超時會被更高優先級進程搶占)的上下文切換為10。
voluntary_ctxt_switches: 1 nonvoluntary_ctxt_switches: 10
-
實時調度發生的情況可能是軟件(如定時器超時)或硬件引發的
-
容器也會使用CFS在各個cgroup中進行調度,更多細節參見understanding-linux-container-scheduling