英語

排程器域

每個 CPU 都有一個“基本”排程域(struct sched_domain)。域層次結構透過 ->parent 指標從這些基本域構建。 ->parent 必須以 NULL 結尾,並且域結構應該是每個 CPU 的,因為它們是無鎖更新的。

每個排程域跨越多個 CPU(儲存在 ->span 欄位中)。域的 span 必須是其子域 span 的超集(如果需要,可以放寬此限制),並且 CPU i 的基本域必須至少跨越 i。每個 CPU 的頂級域通常跨越系統中的所有 CPU,儘管嚴格來說不必如此,但這可能導致某些 CPU 永遠不會被賦予執行任務,除非顯式設定了允許的 CPU 掩碼。排程域的 span 表示“在這些 CPU 之間平衡程序負載”。

每個排程域必須有一個或多個 CPU 組(struct sched_group),這些組從 ->groups 指標組織為迴圈單向連結串列。這些組的 cpumask 的並集必須與域的 span 相同。 ->groups 指標指向的組必須包含該域所屬的 CPU。組可以在 CPU 之間共享,因為它們包含設定後只讀資料。來自任意兩個組的 cpumask 的交集可以是非空的。 如果是這種情況,則在相應的排程域上設定 SD_OVERLAP 標誌,並且其組可能不會在 CPU 之間共享。

排程域內的負載均衡發生在組之間。也就是說,每個組都被視為一個實體。一個組的負載被定義為其每個成員 CPU 的負載之和,只有當一個組的負載變得不平衡時,任務才會在組之間移動。

在 kernel/sched/core.c 中,sched_balance_trigger() 透過 sched_tick() 定期在每個 CPU 上執行。 它會在當前執行佇列的下一個定期排程的重新平衡事件到達後引發一個 softirq。 實際的負載均衡主力,sched_balance_softirq()->sched_balance_domains(),然後在 softirq 上下文 (SCHED_SOFTIRQ) 中執行。

後一個函式接受兩個引數:當前 CPU 的執行佇列以及 sched_tick() 發生時 CPU 是否處於空閒狀態,並迭代我們的 CPU 所在的所有排程域,從其基本域開始,然後向上遍歷 ->parent 鏈。 在執行此操作時,它會檢查當前域是否已耗盡其重新平衡間隔。 如果是這樣,它會在該域上執行 sched_balance_rq()。 然後它檢查父 sched_domain(如果存在),以及父的父等等。

最初,sched_balance_rq() 找到當前排程域中最繁忙的組。 如果成功,它會查詢該組中所有 CPU 的執行佇列中最繁忙的執行佇列。 如果它設法找到這樣的執行佇列,它會鎖定我們的初始 CPU 的執行佇列和新找到的最繁忙的執行佇列,並開始將任務從它移動到我們的執行佇列。 任務的確切數量相當於先前在迭代此排程域的組時計算出的不平衡。

實現排程器域

“基本”域將“跨越”層次結構的第一個級別。 在 SMT 的情況下,您將跨越物理 CPU 的所有兄弟,每個組都是一個虛擬 CPU。

在 SMP 中,基本域的父域將跨越節點中的所有物理 CPU。 每個組都是一個物理 CPU。 然後使用 NUMA,SMP 域的父域將跨越整個機器,每個組都具有一個節點的 cpumask。 或者,例如,您可以執行多級 NUMA 或 Opteron,可能只有一個域覆蓋其一個 NUMA 級別。

實現者應閱讀 include/linux/sched/sd_flags.h 中的註釋:SD_* 以瞭解具體細節以及如何針對 sched_domain 的 SD 標誌進行調整。

架構可以透過建立一個 sched_domain_topology_level 陣列並使用此陣列作為引數呼叫 set_sched_topology() 來覆蓋通用域構建器和給定拓撲級別的預設 SD 標誌。

可以透過將 'sched_verbose' 新增到您的 cmdline 來啟用 sched-domains 除錯基礎設施。 如果您忘記調整您的 cmdline,您也可以翻轉 /sys/kernel/debug/sched/verbose knob。 這會啟用排程域的錯誤檢查解析,該解析應該會捕獲大多數可能的錯誤(如上所述)。 它還會以可視格式打印出域結構。