龍蜥開源Plugsched:首次實現 Linux kernel 排程器熱升級 | 龍蜥技術

OpenAnolis小助手發表於2022-04-01

文/龍蜥社群核心開發人員 陳善佩、吳一昊、鄧二偉

Plugsched 是 Linux 核心排程器子系統熱升級的 SDK, 它可以實現在不重啟系統、應用的情況下動態替換排程器子系統,毫秒級 downtime Plugsched 可以對生產環境中的核心排程特性動態地進行增、刪、改,以滿足不同場景或應用的需求,且支援回滾。

基於 plugsched 實現的排程器熱升級,不修改現有核心程式碼,就能獲得較好的可修改能力,天然支援線上的老核心版本。如果提前在核心排程器程式碼的關鍵資料結構中加入 Reserve 欄位,可以額外獲得修改資料結構的能力,進一步提升可修改能力。
Plugsched 開源連結及其他相關連結可移步龍蜥公眾號(OpenAnolis龍蜥)2022年3月31日相同推送檢視。

那麼   Plugsched 誕生的背景或者想要解決的問題是什麼?我們認為有以下 4 點:

  • 應用場景不同,最佳排程策略不同。 應用種類極大豐富,應用特徵也是千變萬化 (throughput-oriented workloads, ?s-scale latency critical workloads, soft real-time, and energy efficiency requirements),使得排程策略的最佳化比較複雜,不存在“一勞永逸”的策略。因此,允許使用者定製排程器滿足不同的場景是必要的。

  • 排程器迭代慢。 Linux 核心經過很多年的更新迭代,程式碼變得越來越繁重。排程器是核心最核心的子系統之一,它的結構複雜,與其它子系統緊密耦合,這使得開發和除錯變得越發困難。此外,Linux 很少增加新的排程類,尤其是不太可能接受非通用或場景針對型的排程器,上游社群在排程領域發展緩慢。

  • 核心升級困難。排程器內嵌 (built-in)在核心中,上線排程器的最佳化或新特性需要升級核心版本。核心釋出週期通常是數月之久,這將導致新的排程器無法及時應用在生產系統中。再者,要在叢集範圍升級新核心,涉及業務遷移和停機升級,對業務方來說代價昂貴。

  • 無法升級子系統。kpatch 和 livepatch 是函式粒度的熱升級方案,可修改能力較弱,不能實現複雜的邏輯改動;eBPF 技術在核心網路中廣泛應用,但現在排程器還不支援 ebpf hook,將來即使支援,也只是實現區域性策略的靈活修改,可修改能力同樣較弱。

Plugsched 能將排程器子系統從核心中提取出來,以 模組的形式對核心排程器進行熱升級。透過對排程器模組的修改,能夠針對不同業務定製化排程器,而且使用模組能夠更敏捷的開發新特性和最佳化點,並且可以在不中斷業務的情況下上線。

圖1 plugsched: 業務不中斷
使用 plugsched 具有以下 6 大優勢:

  • 與核心釋出解耦:排程器版本與核心版本解耦,不同業務可以使用不同排程策略;建立持續運維能力,加速排程問題修復、策略最佳化落地;提升排程領域創新空間,加快排程器技術演進和迭代

  • 可修改能力強 :可以實現複雜的排程特性和最佳化策略,能人之所不能

  • 維護簡單:不修改核心程式碼,或少量修改核心程式碼,保持核心主線乾淨整潔;在核心程式碼 Tree 外獨立維護非通用排程策略,採用 RPM 的形式釋出和上線

  • 簡單易用:容器化的 SDK 開發環境,一鍵生成 RPM,開發測試簡潔高效

  • 向下相容:支援老核心版本,使得存量業務也能及時享受新技術紅利

  • 高效的效能:毫秒級 downtime,可忽略的 overhead。

Plugsched 應用案例

Plugsched 相比 kpatch 和 livepatch 可修改能力更強,熱升級範圍更廣,plugsched 是子系統範圍的熱升級,而後者是函式級別的熱升級。 對於 plugsched 而言,無論是 bugfix,還是效能最佳化,甚至是特性的增、刪、改,都可勝任。鑑於 plugsched 較強的可修改能力,它可應用到以下場景:

  • 快速開發、驗證、上線新特性,穩定後放入核心主線

  • 針對不同業務場景做定製最佳化,以 RPM 包的形式釋出和維護非通用排程器特性

  • 統一管理排程器熱補丁,避免多個熱補丁之間的衝突而引發故障

應用案例 1:新增 Group Identity 排程特性

Group Identity 是阿里雲用於混部場景的排程特性,它基於 CFS 排程器增加了一顆儲存低優先順序任務的紅黑樹,而且會對每一個 cgroup 分配一個預設優先順序,使用者也可自行配置其優先順序,當佇列中存在高優先順序任務時,低優先順序任務會停止執行。我們利用 plugsched 對 anck 4.19 的一個老版本核心(沒有該排程特性)進行排程器熱升級,並將 Group Identity 排程特性移植到生成的排程器模組中,涉及 7 個檔案,2500+ 行的修改量。

安裝該排程器模組後,在系統中建立兩個 cpu cgroup A 和 B,並繫結同一個 CPU,分別設定最高和最低優先順序,然後各自建立一個 busy loop 任務。理論上,當 A 中有任務執行時,B 中的任務會停止執行。此時用 top 工具檢視該 CPU 利用率,發現只有一個利用率是 100% 的 busy loop 任務,說明模組中的 Group Identity 特性已生效;而動態解除安裝該模組後,出現了兩個各佔 50% CPU 的 busy loop 任務,說明模組已經失效。

應用案例 2:與核心釋出解耦及定製化排程器

阿里雲某客戶使用的舊版本核心,由於該核心排程器對 load 的統計演算法不合理,導致 CPU 利用率過高,雖然修復補丁已經合入核心主線,但是新核心版本還未釋出,而且業務方也不打算更換核心,因為叢集中部署了大量的業務,升級核心成本較高。

除此之外,客戶的核心開發人員對其混部業務場景(Group Identity 排程特性)進行了針對性的最佳化,想將最佳化內容合入核心主線。但是,阿里雲核心開發人員發現,該最佳化內容在其它場景中有效能回退,屬於非通用最佳化,因此不允許將最佳化內容合入主線。
於是,客戶的核心開發人員使用 plugsched 將最佳化修復內容全部移植到排程器模組中,最後規模部署。該案例可以體現出 plugsched 的與核心釋出解耦、定製化排程器的優勢。

那麼 Plugsched  該如何使用?

目前,plugsched 預設支援 Anolis OS 7(核心ANCK-4.19版本) 系統,其它作業系統需要調整邊界配置。為了減輕搭建執行環境的複雜度,我們提供了容器映象和 Dockerfile,開發人員不需要自己去搭建開發環境。為了方便演示,這裡購買了一臺阿里雲 ECS(64CPU + 128GB),並安裝 Anolis OS 7.9 ANCK 系統發行版,我們將演示對其核心排程器進行熱升級的過程。

1、登陸雲伺服器後,先安裝一些必要的基礎軟體包:

# yum install anolis-repos -y # yum install podman kernel-debuginfo-$(uname -r) kernel-devel-$(uname -r) --enablerepo=Plus-debuginfo --enablerepo=Plus -y

2、建立臨時工作目錄,下載系統核心的 SRPM 包:

# mkdir /tmp/work # uname -r 4.19.91-25.2.an7.x86_64 # cd /tmp/work 
# wget 

3、啟動並進入容器:

# podman run -itd --name=plugsched -v /tmp/work:/tmp/work -v /usr/src/kernels:/usr/src/kernels -v /usr/lib/debug/lib/modules:/usr/lib/debug/lib/modules docker.io/plugsched/plugsched-sdk # podman exec -it plugsched bash # cd /tmp/work


4、提取 4.19.91-25.1.al7.x86_64 核心原始碼:

# plugsched-cli extract_src kernel-4.19.91-25.2.an7.src.rpm ./kernel

5、進行邊界劃分與提取:

# plugsched-cli init 4.19.91-25.2.an7.x86_64 ./kernel ./scheduler

6、提取後的排程器模組程式碼在 ./scheduler/kernel/sched/mod 中,簡單修改 __schedule 函式,然後編譯打包成排程器 rpm 包:

diff --git a/kernel/sched/mod/core.c b/kernel/sched/mod/core.c
index f337607..88fe861 100644
--- a/kernel/sched/mod/core.c
+++ b/kernel/sched/mod/core.c
@@ -3235,6 +3235,8 @@ static void __sched notrace __schedule(bool preempt)
   struct rq *rq;
   int cpu;
 
+  printk_once("scheduler: Hi, I am the new scheduler!\n");
+
   cpu = smp_processor_id();
   rq = cpu_rq(cpu);
   prev = rq->curr;
   
   
# plugsched-cli build /tmp/work/scheduler

7、將生成的 rpm 包複製到宿主機,退出容器,並安裝排程器包,排程器日誌顯示新修改的排程器已經生效:

# cp /usr/local/lib/plugsched/rpmbuild/RPMS/x86_64/scheduler-xxx-4.19.91-25.2.an7.yyy.x86_64.rpm /tmp/work
# exit
exit
# rpm -ivh /tmp/work/scheduler-xxx-4.19.91-25.2.an7.yyy.x86_64.rpm
# dmesg | tail -n 10
[  878.915006] scheduler: total initialization time is        5780743 ns
[  878.915006] scheduler module is loading
[  878.915232] scheduler: Hi, I am the new scheduler!
[  878.915232] scheduler: Hi, I am the new scheduler!
[  878.915990] scheduler load: current cpu number is               64
[  878.915990] scheduler load: current thread number is           626
[  878.915991] scheduler load: stop machine time is            243138 ns
[  878.915991] scheduler load: stop handler time is            148542 ns
[  878.915992] scheduler load: stack check time is              86532 ns
[  878.915992] scheduler load: all the time is                 982076 ns


我們透過以上知道了 Plugsched 是什麼、應用案例,那它實現原理是什麼?

排程器子系統在核心中並非是一個獨立的模組,而是內嵌在核心中,與核心其它部分緊密相連。 Plugsched 採用“模組化”的思想:它提供了邊界劃分程式,確定排程器子系統的邊界,把排程器從核心程式碼中提取到獨立的目錄中,開發人員可對提取出的排程器程式碼進行修改,然後編譯成新的排程器核心模組,動態替換核心中舊的排程器。對子系統進行邊界劃分和程式碼提取,需要處理函式和資料,而後生成一個獨立的模組。
對於函式而言,排程器模組對外呈現了一些關鍵的函式,以這些函式為入口就可以進入模組中,我們稱之為介面函式。透過替換核心中的這些介面函式,核心就可以繞過原有的執行邏輯進入新的排程器模組中執行,即可完成函式的升級。模組中的函式,除了介面函式外,還有內部函式,其它的則是外部函式。
對於資料,排程器模組預設使用並繼承核心中原有的資料,對於排程器重要的資料,比如執行佇列狀態等,可以透過狀態重建技術自動重新初始化,這類資料屬於私有資料,而其它則是共享資料。為了靈活性,plugsched 允許使用者手動設定私有資料,手動設定的私有資料會在模組中保留定義,但需要對它們進行初始化。對於結構體而言,plugsched 將只被排程器訪問的結構體成員分類為內部成員,其它為非內部成員。排程器模組允許修改內部成員的語義,禁止修改非內部成員的語義。如果結構體所有成員都是內部成員,則排程器模組允許修改整個結構體。但是,建議優先使用結構體中的保留欄位,而不是修改現有成員。
設計方案

Plugsched 主要包含兩大部分,第一部分是排程器模組邊界劃分與程式碼提取部分,第二部分是排程器模組熱升級部分,這兩部分是整個方案的核心。其整體設計方案如下:


圖2 plugsched 整體架構

首先進行的是排程器模組邊界劃分和程式碼提取流程,由於排程器本身並不是模組,因此需要明確排程器的邊界才能將它模組化。邊界劃分程式會根據邊界配置資訊(主要包含程式碼檔案、介面函式等資訊)從核心原始碼中將排程器模組的程式碼提取到指定目錄,然後開發人員可在此基礎上進行排程器模組的開發,最後編譯生成排程器 RPM 包,並可安裝在對應核心版本的系統中。安裝後會替換掉核心中原有的排程器,安裝過程會經歷以下幾個關鍵過程:

  • 符號重定位: 解析模組對部分核心符號的訪問

  • 棧安全檢查: 類似於 kpatch,函式替換前必須進行棧安全檢查,否則會出現當機的風險。plugsched 對棧安全檢查進行了並行和二分最佳化,提升了棧安全檢查的效率,降低了停機時間

  • 介面函式替換: 用模組中的介面函式動態替換核心中的函式

  • 排程器狀態重建: 採用通用方案自動同步新舊排程器的狀態,極大的簡化資料狀態的一致性維護工作

總結: 基於以上介紹,整體來看,Plugsched 使得排程器從核心中解放出來,開發人員可以對排程器進行專項定製,而不侷限於核心通用排程器;核心維護也變得更加輕鬆,因為開發人員只需要關注通用排程器的開發與迭代,定製化排程器可透過 RPM 包的形式進行釋出;核心排程器程式碼也會變得簡潔,無需再被各個場景的最佳化混淆起來。

未來,plugsched 會支援新版本核心和其它平臺,持續對其易用性進行最佳化,並提供更多的應用案例。最後,歡迎更多的開發者能參與到 plugsched 中。

—— 完 ——

加入龍蜥社群

加入微信群:新增社群助理-龍蜥社群小龍(微信:openanolis_assis),備註【龍蜥】與你同在;加入釘釘群:掃描下方釘釘群二維碼。歡迎開發者/使用者加入龍蜥社群(OpenAnolis)交流,共同推進龍蜥社群的發展,一起打造一個活躍的、健康的開源作業系統生態!

關於龍蜥社群

龍蜥社群OpenAnolis)是由 企事業單位、高等院校、科研單位、非營利性組織、個人等在自願、平等、開源、協作的基礎上組成的非盈利性開源社群。龍蜥社群成立於 2020 年 9 月,旨在構建一個開源、中立、開放的Linux 上游發行版社群及創新平臺。

龍蜥社群成立的短期目標是開發龍蜥作業系統(Anolis OS)作為 CentOS 停服後的應對方案,構建一個相容國際 Linux 主流廠商的社群發行版。中長期目標是探索打造一個面向未來的作業系統,建立統一的開源作業系統生態,孵化創新開源專案,繁榮開源生態。

目前, 龍蜥OS 8.4 已釋出,支援 X86_64 、Arm64、LoongArch 架構,完善適配飛騰、海光、兆芯、鯤鵬、龍芯等晶片,並提供全棧國密支援。

歡迎加入我們,一起打造面向未來的開源作業系統!


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70004278/viewspace-2885428/,如需轉載,請註明出處,否則將追究法律責任。

相關文章