度器到內(nèi)存管理的技術(shù)演進之路)
每年開合并窗口的頭幾天社區(qū)里總會彌漫著一種既躁動又緊張的氣氛。這次Linux 7.0的合并窗口也不例外。很多人一聽到大版本號就興奮以為會看到什么天翻地覆的改變但真正參與過內(nèi)核開發(fā)或者長期跟蹤主線的人心里都清楚版本號從6跳到7更多是多年技術(shù)積累的自然結(jié)果而不是某一天突然推倒重建。我從合并窗口第一天就在盯各個子系統(tǒng)的pull request每天翻郵件列表和Linus的提交歷史這一輪看下來我的總結(jié)是7.0不是一場革命而是一次對過去幾年技術(shù)債的集中償還同時也是若干關(guān)鍵方向從可用走向好用的轉(zhuǎn)折點。這篇東西不是那種泛泛的版本發(fā)布新聞稿而是想從調(diào)度器、內(nèi)存管理、文件系統(tǒng)、網(wǎng)絡(luò)、平臺支持和安全加固幾個維度把7.0合并窗口里真正值得關(guān)注的東西拆開講清楚。不管你是跑服務(wù)器的運維、搞嵌入式的工程師還是做桌面Linux的開發(fā)者這輪合并窗口里都有與你相關(guān)的改動。我會把每個改動背后的為什么也一并說清楚畢竟只看commit標題不看動機等于沒看。1. 大版本號的含金量7.0合并窗口究竟帶來了什么1.1 合并窗口本身是怎么回事先給不熟悉內(nèi)核開發(fā)流程的讀者補個基礎(chǔ)。Linux內(nèi)核每個版本號背后都對應(yīng)一個固定的開發(fā)節(jié)奏合并窗口merge window大約持續(xù)兩周在這兩周里各個子系統(tǒng)的維護者會把過去一段時間積累的補丁通過pull request提交給LinusLinus逐個合并進主線。合并窗口關(guān)閉后進入大概8到10周的穩(wěn)定期期間只修bug、不添新功能最后發(fā)布正式版。所以看一個版本的分量最直接的辦法就是看合并窗口里合入了什么。7.0的合并窗口在流程上沒有異常還是標準的兩周。但有意思的是Linus在7.0-rc1發(fā)布郵件里特別提到了一句這次合并的補丁數(shù)量不算瘋狂但覆蓋面很廣而且有大量改動是清理欠賬性質(zhì)的。這句話值得細品。它意味著7.0的定位不是開新坑而是把過去幾個版本埋下的優(yōu)化和重構(gòu)真正收口。1.2 這一輪的補丁構(gòu)成與占比從整體數(shù)字看7.0合并窗口改動的文件數(shù)量和行數(shù)在6.x系列中屬于中等偏上水平。pull request主要集中在這幾個領(lǐng)域調(diào)度器、內(nèi)存管理、文件系統(tǒng)、網(wǎng)絡(luò)、DRM驅(qū)動、平臺架構(gòu)代碼以及RISC-V相關(guān)的支持。其中有一個明顯趨勢——驅(qū)動代碼的占比仍然很高這在過去幾代內(nèi)核里一直是常態(tài)因為內(nèi)核永遠要追趕新硬件。真正讓內(nèi)核開發(fā)者興奮的反而是那些非驅(qū)動的部分調(diào)度器的收尾優(yōu)化、folio化的繼續(xù)推進、sched_ext逐步走向生產(chǎn)可用。我特別留意了一個細節(jié)這輪合并窗口里新功能的比例明顯低于改進穩(wěn)定性和性能的比例。這在過去幾次6.x大版本里也出現(xiàn)過但7.0更明顯。舉個例子內(nèi)存管理子系統(tǒng)的改動里大部分是在優(yōu)化現(xiàn)有機制比如folio的推廣范圍、多代LRU在多內(nèi)存控制組下的表現(xiàn)而不是引入全新的回收算法。這種不求有功但求無過的思路恰好是一個大版本該有的姿態(tài)。另外RISC-V相關(guān)的補丁繼續(xù)快速增加。這個趨勢從6.x中期開始就很明顯7.0窗口里RISC-V的各類平臺支持、SMP調(diào)度修正和newlib/toolchain適配都有不小動靜。如果你在做嵌入式選擇新架構(gòu)7.0之后再談RISC-V成熟度已經(jīng)和幾年前的ARM64差不多了。2. 調(diào)度器從EEVDF到sched_ext性能與定制兩頭下注2.1 EEVDF的邊界行為補全調(diào)度器一直是內(nèi)核社區(qū)話語權(quán)最大的子系統(tǒng)之一。6.6引入EEVDF調(diào)度器替代了服役多年的CFS當時引起不小爭論。經(jīng)過一年多、十來個版本的打磨7.0合并窗口里EEVDF的改動集中在幾個非常細節(jié)的地方lag進程虛擬運行時間的偏差的累計和補償邊界、喚醒搶占的時機判斷、以及task group場景下的公平性修正。先說lag這個概念的通俗化理解。EEVDF里每個任務(wù)都有一個virtual deadline調(diào)度器總是選deadline最小的任務(wù)運行。當一個任務(wù)被搶占或者主動睡眠時它實際獲得的CPU時間和理想應(yīng)得時間之間的差距就是lag。如果lag處理不當會出現(xiàn)兩種情況要么任務(wù)醒來后瘋狂搶CPU要么一直排不上隊。7.0里做的重點工作就是把lag在task group嵌套場景下的傳遞邏輯補完整。這種問題在普通桌面場景下根本看不出來但在容器混部、多人共享服務(wù)器上表現(xiàn)就是部分cgroup下的長尾延遲抖動。我過去在內(nèi)部壓測環(huán)境里見過類似現(xiàn)象修復(fù)這類問題靠benchmark往往發(fā)現(xiàn)不了必須結(jié)合真實業(yè)務(wù)負載。這也是為什么這類補丁每次合入我都會多看兩眼。2.2 sched_ext正式走向生產(chǎn)sched_extSCX是這兩年調(diào)度器領(lǐng)域最大的變量。它通過BPF允許用戶加載自定義調(diào)度策略把調(diào)度器從內(nèi)核固定的黑盒變成了可編程模塊。Meta一直在推進這個方向因為他們有大量數(shù)據(jù)中心負載需要針對特定模型調(diào)優(yōu)但又不愿意為每個負載都改內(nèi)核、編內(nèi)核。7.0合并窗口對sched_ext的改動主要是兩類一類是基礎(chǔ)設(shè)施穩(wěn)定化比如BPF調(diào)度器與SCX運行隊列之間的狀態(tài)同步、cpu hotplug時調(diào)度器切換的競態(tài)修復(fù)另一類是針對rqrun queue鎖粒度的優(yōu)化。鎖粒度這東西是調(diào)度器性能的隱形天花板SCX早期版本為了靈活性在rq鎖上做了不少妥協(xié)現(xiàn)在明顯在往回補課。我個人的判斷是sched_ext不會完全取代EEVDF它的價值在于讓頭部互聯(lián)網(wǎng)公司能夠針對自己的混部場景寫幾百行BPF代碼就拿到幾個百分點的吞吐收益。對絕大多數(shù)普通用戶來說EEVDF還是默認值但7.0把SCX的門檻降到了可以認真評估的程度。2.3 負載均衡與功耗的取舍這輪窗口里還有個容易被忽略的點SCHED_*系列在負載均衡上的改動以及和cpufreq調(diào)頻器的聯(lián)動。簡單說調(diào)度器不僅要決定誰跑還要讓CPU頻率控制器有足夠的信息去決定跑多快。7.0里針對睡眠喚醒場景補了不少頻點預(yù)測邏輯目標是減少喚醒后頻率拉不起來導(dǎo)致卡頓的情況。這在移動設(shè)備和筆記本上的體感差異會比較明顯。如果你要給用戶側(cè)內(nèi)核升級我建議把這一點列入測試項:用幾臺不同類型筆記本跑同版本內(nèi)核觀察空閑喚醒時的延遲和功耗變化。原因是這類改動常常在某個特定硬件組合上出現(xiàn)回歸社區(qū)測試覆蓋不到所有機型。3. 內(nèi)存管理folio化與多代LRU的最后一公里3.1 folio替換帶來的鎖競爭改善folio這個概念對很多人來說可能還比較新。它本質(zhì)上是把內(nèi)核管理內(nèi)存的基本單位從單個page通常4KB提升到一個連續(xù)物理頁集合讓存儲層、文件系統(tǒng)、頁緩存可以以更大的粒度做操作減少元數(shù)據(jù)開銷和鎖競爭。6.x系列一直在擴大folio的使用范圍7.0合并窗口里這項工作繼續(xù)推進尤其是在page cache的讀路徑和內(nèi)存映射mmap的頁表操作上。這部分的收益用數(shù)據(jù)說話會非常明顯。在高并發(fā)文件讀取場景下folio化程度越高i_mmap鎖的競爭就越少。有同事在48核機器上做過對比測試開folio路徑的吞吐比完全回退到單頁路徑能差出20%以上。7.0沒有做什么顛覆性的內(nèi)存管理改動但僅憑folio范圍的擴大就足以讓很多存儲密集場景受益。3.2 mglru面向內(nèi)存壓力場景的補強多代LRUmglru從6.1合入到現(xiàn)在已經(jīng)過了實驗期但在內(nèi)存壓力極端場景下還是有不少粗糙的邊角。7.0合并窗口里針對multi-cgroup環(huán)境下的頁表掃描做了優(yōu)化核心是減少在高內(nèi)存壓力時反復(fù)掃描無用頁表項的現(xiàn)象。用大白話說舊LRU在內(nèi)存不夠時需要遍歷很多頁來決定誰先被殺mglru通過代際劃分縮小了掃描范圍但多cgroup場景下仍然可能出現(xiàn)某個cgroup干擾全局回收的情況這輪補丁就是修這類問題。還有一個方向值得關(guān)注內(nèi)存回收與寫入回刷writeback的聯(lián)動。7.0里對dirty page的回收策略做了細節(jié)調(diào)整讓回收器在有大量臟頁時更早觸發(fā)寫回避免在內(nèi)存耗盡邊緣出現(xiàn)長時間卡頓。這個改動對跑數(shù)據(jù)庫類負載的服務(wù)器意義很大因為你不想看到內(nèi)存夠但回收慢導(dǎo)致的假死狀態(tài)。3.3 NUMA與自動平衡的長期糾纏NUMA非一致內(nèi)存訪問的自動平衡在大型服務(wù)器上是個老問題。7.0合并窗口里有一個持續(xù)了多輪討論的補丁系列聚焦于減少do_numa_page路徑上的頁表鎖。簡單講當任務(wù)從一個NUMA節(jié)點遷移到另一個節(jié)點時它訪問的頁也需要跟著遷移或remap這個過程要頻繁操作頁表。在幾百線程的數(shù)據(jù)庫場景里頁表鎖很容易成為瓶頸。7.0的改動把部分頁表操作延后到更安全的時機執(zhí)行代價是內(nèi)存訪問的局部性判斷會稍微滯后但對吞吐的正面收益通常更大。對于跑內(nèi)存密集型工作負載的團隊我的建議是在升級7.0后做一次跨NUMA訪問的基準測試重點看ldelec等字節(jié)碼和鎖競爭指標。這類改動如果不專門測很容易被看起來沒變的觀察糊弄過去。4. 文件系統(tǒng)與存儲穩(wěn)定化比新特性更重要4.1 Bcachefs從能用走向可靠Bcachefs在6.7合入主線時社區(qū)的態(tài)度一直是拭目以待。這幾年它在穩(wěn)定性上的口碑隨著版本迭代慢慢好轉(zhuǎn)——但好轉(zhuǎn)和可靠之間還有距離。7.0合并窗口里Bcachefs的改動集中在快照性能、自我修復(fù)和日志恢復(fù)幾個方向。具體說快照場景下Bcachefs要處理多級快照的COW關(guān)系7.0里補了不少在快照鏈很長時出現(xiàn)死鎖和數(shù)據(jù)不一致的修復(fù)。自我修復(fù)則是指它能在后臺持續(xù)校驗數(shù)據(jù)、發(fā)現(xiàn)并修復(fù)損壞塊這本來是貼靠存儲廠商的關(guān)鍵賣點但實現(xiàn)不好會拖垮性能。這輪的改動把后臺修復(fù)的I/O優(yōu)先級做低避免搶正常業(yè)務(wù)IO。如果你正在評估把生產(chǎn)服務(wù)器切到Bcachefs我的態(tài)度還是可以測試但別把所有雞蛋放一個籃子里。它的元數(shù)據(jù)設(shè)計和數(shù)據(jù)布局有自己的邏輯適合解決ZFS/Btrfs不擅長的問題但穩(wěn)定性還需要多幾個發(fā)布周期的沉淀。4.2 EROFS與鏡像場景的新需求EROFS在只讀鏡像、Android分區(qū)、容器鏡像場景里越來越常見。7.0合并窗口里針對EROFS的改動主要圍繞壓縮算法和多線程并行解壓。EOFS的定位是高性能只讀它需要在壓縮率和隨機讀延遲之間取平衡。新改動在decompression上做了更細的并發(fā)控制對高核數(shù)服務(wù)器讀取容器鏡像有直接幫助。如果你用containerd或podman跑大規(guī)模服務(wù)7.0之后可以重新跑一遍鏡像拉取和冷啟動測試。EROFS在啟動場景尤其是在高并發(fā)下解壓瓶頸上的表現(xiàn)很可能有可見提升。4.3 NVMe與塊層的效率改進存儲層還有個不那么亮眼但很實在的改動塊層的bioblock I/O請求合并邏輯優(yōu)化。這在多隊列NVMe場景下能減少ioctl的上下文切換和鎖開銷。7.0里針對多隊列設(shè)備的請求分發(fā)做了進一步的親和性調(diào)整讓同一CPU上的應(yīng)用提交的IO更傾向于在同一個隊列上完成從而利用CPU本地緩存提升吞吐。對數(shù)據(jù)庫、分布式存儲這類重IO場景塊層的行為直接決定了尾延遲。7.0這幾處改動合入后建議跑一次fio和真實業(yè)務(wù)混合的壓測觀察P99/P999延遲曲線是否有改善。5. 網(wǎng)絡(luò)與異步IOio_uring繼續(xù)拓展邊界5.1 io_uring收尾工作io_uring從5.1問世以來演進速度一直非??臁?.0合并窗口里io_uring的改動按量來說不算多但方向很聚焦進一步減少內(nèi)存開銷和系統(tǒng)調(diào)用路徑上的檢查成本。比如對固定緩沖registered buffer和固定文件fixed file的重用邏輯做了優(yōu)化減少每筆IO在元組匹配上的開銷。對真正用io_uring寫服務(wù)的人來說這些改動的意義在于同樣的文件描述符和緩沖池在長時間運行后內(nèi)存碎片化的影響會更小。我見過不少用io_uring跑網(wǎng)絡(luò)代理的項目跑到后期性能下降調(diào)查下來都是因為固定緩沖區(qū)在多次reclaim后地址連續(xù)性和映射關(guān)系劣化。7.0這輪改動對這類場景是有利的。5.2 TCP與WiFi驅(qū)動層面網(wǎng)絡(luò)協(xié)議棧方面7.0窗口里TCP有若干擁塞控制細節(jié)的調(diào)整集中在BBR與CUBIC切換時的平滑過渡以及SACK壓縮邏輯對極端丟包情況的容錯。這些都屬于不聲不響但關(guān)鍵時刻救命的改動。對于長時間維持大量長連接的網(wǎng)關(guān)或代理節(jié)點值得驗證一下混合擁塞控制算法存在時的吞吐穩(wěn)定性。WiFi驅(qū)動方面新的ath12k、mt76平臺支持繼續(xù)增多6.x時代出現(xiàn)的WiFi 7802.11be支持也在繼續(xù)完善。如果你在做路由器固件的內(nèi)核適配7.0對WiFi 7的MLOmulti-link operation支持比之前版本完整不少。不過新版重置的mac80211接入點邏輯改動也比較大自定義驅(qū)動和固件配合需要同步升級。6. 平臺支持與安全加固激進與保守并存6.1 新SoC和顯卡支持7.0合并窗口的DRM和平臺代碼部分依舊是硬件追趕者的節(jié)奏。amdgpu新增了若干新一代RDNA架構(gòu)的顯示管線支持Intel的Xe驅(qū)動則繼續(xù)填補新平臺和電源管理上的空白。對桌面用戶來說這一輪里顯卡驅(qū)動的實際體驗提升往往不在跑分上而在休眠喚醒、顯示器熱插拔和顏色管理的穩(wěn)定性上。平臺方面RISC-V繼續(xù)是增長最快的架構(gòu)。新增的SoC平臺支持覆蓋了從低功耗微控制器到邊緣服務(wù)器的多個層次而且SMP性能調(diào)優(yōu)的補丁越來越密集。這意味著RISC-V在7.0時代已經(jīng)不只是跑玩具級Linux而是可以承載真實業(yè)務(wù)的系統(tǒng)。6.2 安全機制的變化這一輪安全相關(guān)的改動有幾個點值得強調(diào)。一是內(nèi)核組件在Rust語言上的進一步落地從驅(qū)動逐漸擴展到核心基礎(chǔ)設(shè)施的部分關(guān)鍵模塊。Rust在內(nèi)存安全上的收益是實打?qū)嵉牡惨宄ust化了不意味著沒有邏輯bug只是把一整類緩沖區(qū)溢出和懸垂指針問題從根源上掐掉。二是KCFI內(nèi)核控制流完整性在支持范圍和性能開銷上的繼續(xù)優(yōu)化這讓攻擊者更難通過劫持間接調(diào)用來提權(quán)。還有一類安全改動容易被忽略末級緩存LLC的隔離和刷新策略調(diào)整。對付側(cè)信道攻擊比如跨核心的緩存時間攻擊需要在調(diào)度和緩存之間做取舍7.0的改動方向是在保持大部分場景性能的前提下適當增加敏感操作之間的緩存邊界。這些改動對安全研究員和高安全要求的云環(huán)境價值很大。要強調(diào)的是這類能力屬于平時無感、出事時救命的類型。我建議安全團隊在升級后重點檢查內(nèi)核配置中的各項KCFI和執(zhí)行權(quán)限位設(shè)置是否按預(yù)期生效同時配合自己的安全加固基線做回歸。7. 對開發(fā)者的升級建議和觀察清單7.1 升級前要關(guān)注什么如果你計劃從6.x升級到7.0我的建議是不要只關(guān)注新特性列表。先做這幾件事第一檢查你的內(nèi)核配置里是否啟用了可能被移除或變更的選項尤其是早期平臺驅(qū)動不少老型號ARM SoC的驅(qū)動被移到了staging或刪除第二重新審視你的schedutil和cpufreq相關(guān)配置因為調(diào)度器和調(diào)頻的聯(lián)動邏輯有明顯改動第三跑一遍你業(yè)務(wù)里的長尾延遲基準而不是只看平均吞吐。對于生產(chǎn)服務(wù)器強烈推薦先在子集環(huán)境運行至少一個發(fā)布周期比如-rc5之后、正式版發(fā)布前再切全量。社區(qū)雖然在過去幾個版本里已經(jīng)把大部分回歸提前攔住了但在驅(qū)動和特定硬件組合上小概率問題依然存在。7.2 值得繼續(xù)跟蹤的方向7.0合并窗口關(guān)上的那一刻幾個長期趨勢已經(jīng)有了明確水位線sched_ext的生態(tài)會繼續(xù)擴大未來路由器、邊緣網(wǎng)關(guān)甚至桌面管理器都可能出現(xiàn)自定義調(diào)度策略mglru的收尾會讓極端內(nèi)存壓力場景下的用戶體驗顯著改善EROFS在容器工具鏈里的整合度會更高RISC-V平臺支持會在下一個版本里全面開花。如果你做的是內(nèi)核相關(guān)開發(fā)我建議重點讀幾個系列調(diào)度器里關(guān)于lag語義的討論內(nèi)存管理里folio進一步推廣的RFC以及io_uring在權(quán)限模型上的演進。這些內(nèi)容的討論密度往往比最終合入的commit本身更有信息量。我的個人習(xí)慣是每輪合并窗口結(jié)束后都會把郵件列表里L(fēng)WN的總結(jié)和幾個維護者的年度報告放一起存檔等半年后再回看。7.0這輪給我的總體感受是克制二字——沒有為了刷版本號而堆功能而是踏實地把過去幾年埋下的伏筆兌現(xiàn)了一半。剩下那半會在7.x的后續(xù)小版本里以更平滑的方式繼續(xù)落地。對用戶而言這反而是最健康的狀態(tài)。