天天看點

linux線程排程政策

linux線程排程政策

這是一篇非常好的關于線程排程的資料,翻譯自shed

目錄

    • Scheduling policies
      • SCHED_FIFO: First in-first out scheduling(實時線程)
      • SCHED_RR: Round-robin scheduling(輪詢排程)
      • SCHED_DEADLINE: Sporadic task model deadline scheduling
      • SCHED_OTHER: Default Linux time-sharing scheduling(預設政策)
        • The nice value
      • SCHED_BATCH: Scheduling batch processes
      • SCHED_IDLE: Scheduling very low priority jobs
    • 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
      • The nice value and group scheduling
    • 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擴充的一部分。

核心子產品使用排程器來決定下一個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

僅适用于靜态優先級大于0的線程,即當一個

SCHED_FIFO

的線程變為可運作(runnable)狀态時,它會立即搶占所有目前運作的

SCHED_OTHER

SCHED_BATCH

SCHED_IDLE

線程。

SCHED_FIFO

不使用時間片進行排程,所有使用

SCHED_FIFO

排程政策的線程應該遵守如下規則:

  1. 當一個運作中的

    SCHED_FIFO

    線程被其他有更高優先級的線程搶占後,該線程會傳回到其優先級對應的清單的首部,當所有更高優先級的線程阻塞後,該線程将會立即恢複運作;
  2. 當一個阻塞的

    SCHED_FIFO

    線程變為可運作狀态時,該線程會傳回到其優先級對應的清單末尾;
  3. 如果調用 sched_setscheduler(2),sched_setparam(2),sched_setattr(2),pthread_setschedparam(3),pthread_setschedprio(3) (通過pid)修改了正在運作或可運作狀态的

    SCHED_FIFO

    線程的優先級時,該線程在清單中的位置取決于優先級的變動:
    • 如果線程優先級增加了,它将會放置到新優先級對應的清單末尾,同時可能搶占正在運作的具有相同優先級的線程;
    • 如果線程優先級沒變,其在運作清單中的位置不變;
    • 如果線程優先級減小了,它将會放置到新優先級對應的清單的前面。

      根據POSIX.1-2008,通過非 pthread_setschedprio(3)方式來修改線程的優先級,可能會導緻其放置到對應優先級清單的末尾。

  4. 調用了sched_yield(2) (用于釋放CPU)的線程将會放置到清單末尾

SCHED_FIFO

線程将會一直運作,直到被更高優先級的線程搶占,或調用了sched_yield(2) 。

SCHED_RR

SCHED_FIFO

做了簡單增強。除每個線程僅允許運作在一個最大時間段下外,

SCHED_FIFO

中的所有規則都适用于

SCHED_RR

。如果一個

SCHED_RR

線程已經運作了等于或大于該最大時間段時,該線程會被放置到其優先級清單的末尾。當一個

SCHED_RR

線程被更高優先級的線程搶占,并在後續恢複運作後,會在先前未過期的時間段下運作。最大時間段可以通過sched_rr_get_interval(2)獲得。

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

隻能在靜态優先級為0時使用(普通線程)。

SCHED_OTHER

是标準的Linux分時排程政策(不需要實時機制)。

如何從靜态優先級為0的清單中選擇運作的線程取決于清單中的

dynamic

優先級。

dynamic

優先級基于nice值,且在每次線程準備運作時增加。這種機制保證公平處理所有的

SCHED_OTHER

在Linux核心源碼中,

SCHED_OTHER

被稱為

SCHED_NORMAL

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。

從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.

從Linux 2.6.23開始,

SCHED_IDLE

可以用于靜态優先級為0的線程。nice值不會影響該政策。

該政策用于運作非常低優先級的任務(低于nice值為+19的

SCHED_OTHER

SCHED_BATCH

政策)。

每個線程都有一個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辨別。

在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)擷取更多資訊。

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

    使用

一個阻塞的高優先級的線程(在排程前)等待I/O時會有一個确定的響應時間。裝置驅動作者可以使用"slow interrupt"中斷句柄來減少響應時間

子程序會通過fork(2)繼承排程政策和參數。可以使用execve(2)來儲存排程政策和參數。

實時程序通常會使用memory locking特性來防止記憶體頁的延遲。可以使用mlock(2) 或mlockall(2)設定memory locking。

從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政策分組。

當排程非實時程序時,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

從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排程政策會用于運作具有實時優先級且最小排程延遲的線程。

  • 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寄存器資訊,程序狀态以及記憶體管理等資訊,在程序恢複時還原這些資訊。
linux線程排程政策
  • 可以通過/proc/$pid/status檢視程序上下文切換的情況,如下表示自發(如I/O等待)的上下文切換為1,非自發(如時間片逾時會被更高優先級程序搶占)的上下文切換為10。
    voluntary_ctxt_switches:        1
    nonvoluntary_ctxt_switches:     10
               
  • 實時排程發生的情況可能是軟體(如定時器逾時)或硬體引發的
  • 容器也會使用CFS在各個cgroup中進行排程,更多細節參見understanding-linux-container-scheduling

  • Linux CFS排程器