2023年度盤點|2023年Linux內(nèi)核十大技術(shù)革新功能
發(fā)布時間:2024-01-22 14:21:28
2023年,眾多Linux內(nèi)核開發(fā)者仍然在調(diào)度器、內(nèi)存管理、文件系統(tǒng)等領(lǐng)域貢獻著自己的idea和patch,本文從其中選取十個最典型的patchset,進行闡述,它們是:
基于eBPF的sched_ext調(diào)度類擴展
per-VMA lock
NUMA系統(tǒng)上kernel代碼段復(fù)制
Large folios/動態(tài)大頁
文件系統(tǒng)large block支持
基于scope的資源管理
用代理執(zhí)行解決優(yōu)先級反轉(zhuǎn)(priority inversion)問題
延后用戶空間臨界區(qū)內(nèi)的搶占
EEVDF調(diào)度
BPF通用迭代器
下面我們一一展開。
基于eBPF的sched_ext調(diào)度類擴展
這一patchset的開發(fā)過程,堪稱神仙打架。對壘的多方,無論是發(fā)patch的還是review patch的,都是內(nèi)核社區(qū)的頂流大神,甚至連看客都會北冥神功。他們之間直接的拼殺,刺刀見紅,毫不留情,讓凡人們見識了神仙也有性格,技術(shù)和思想的力量可以怎樣無視虛偽和矯情。
sched_ext patchset由社區(qū)鼎鼎大名的Tejun Heo發(fā)出,他是Linux內(nèi)核cgroup、KERNFS、PER-CPU MEMORY ALLOCATOR、WORKQUEUE等的maintainer。
這個patchset——sched: Implement BPF extensible scheduler class
Patchset的實際貢獻還包括來自Google、meta、卡內(nèi)基梅隆大學(xué)等多家主流廠商和科研院校的開發(fā)者。該patchset擴展了一個調(diào)度class,與之前的CFS、realtime等并行,但是它允許調(diào)度行為被一個BPF程序來實現(xiàn),并聲稱有如下三大好處:
1.讓探索和實驗變地容易: 讓新的調(diào)度策略可以快速迭代
2. 定制化調(diào)度行為:為特定應(yīng)用定制調(diào)度器(這個調(diào)度器也許不適用于通用目的)
3. 調(diào)度器快速部署: 在產(chǎn)品環(huán)境下,非侵入式地修改調(diào)度器。
新加入的sched_ext與內(nèi)核已經(jīng)存在的stop_sched_class、dl_sched_class、rt_sched_class、fair_sched_class、idle_sched_class是一種并列關(guān)系,任何一個sched_class,都需要實現(xiàn)一系列的callback函數(shù),比如:
enqueue_task:將task放入runqueue,在CFS中task會加入一個紅黑樹;
dequeue_task:將task從runqueue拿出來;
pick_next_task:調(diào)度時候選取下一個run的task,比如對于rt調(diào)度類而言就是找bitmap上第一個bit的queue里面的task;
task_tick:在調(diào)度tick發(fā)生時被調(diào)用,比如對于CFS而言,它會更新當(dāng)前運行task的vruntime和sum_exec_runtime,并可能設(shè)置need_resched;
wakeup_preempt:一個task被喚醒的時候(也可能是調(diào)度策略或者優(yōu)先級更改,比如從其他策略調(diào)整為CFS插入CFS的runqueue的switched_to_fair),可能搶占正在運行的task;
select_task_rq:比如fork一個新的task以及exec、wakeup等負(fù)載均衡場景,我們要選擇把task放到哪個CPU的runqueue上面。
Patchset定義了一組可以由eBPF程序?qū)崿F(xiàn)的callback:
并在內(nèi)核的sched_ext class的callback中,調(diào)用這一組eBPF實現(xiàn)的callback,比如ext sched_class的select_task_rq() callback調(diào)用eBPF的select_cpu() callback:
進一步地,由于eBPF程序可以通過maps和userspace交互,實際上,調(diào)度行為也可以在userspace實現(xiàn)了,這讓內(nèi)核sched_class、eBPF的sched_ext_ops和用戶空間,實現(xiàn)了3位一體的聯(lián)動。
比如eBPF中可以pop一個BPF_MAP_TYPE_QUEUE類型的map:
而userspace則可以update_elem相關(guān)dispatch進程的pid到這個map:
整個patchset讓Linux內(nèi)核調(diào)度器的維護者Peter Zijlstra(同時也是ATOMIC INFRASTRUCTURE、CPU HOTPLUG、FUTEX、LKMM、MMU GATHER AND TLB INVALIDATION、Perf等的維護者)所極度反感,在patchset中直接給出了NACK:
他NAK的無疑是一位大神,當(dāng)我們回眸特洛伊之戰(zhàn)中兩位偉大英雄阿喀琉斯和赫克托耳的決斗時刻,最后命運的天平無論便向的是哪一邊,剩下的都只有悲壯。
但是之前我們在page fault中,也是要拿mmap_sem讀鎖的,因為我們也不知道page fault處理過程中,對應(yīng)的VMA會不會變化或者甚至消失,所以要和可能寫VMA的人排他。Page fault的處理邏輯實際是:
由于mmap_sem是整個進程的,而一個進程里面說不定也有成千上萬的VMA,然后大量的page fault以及其他的VMA的寫操作行為,相互競爭鎖,就導(dǎo)致大量的競爭延遲。其他需要持有寫鎖的地方也是非常多的,比如:brk、stack expand、munmap、remap_file_pages、exit、madvise、mprotect、mremap、mlock等。
用一個大的mmap_lock把這些寫和page fault的讀進行保護,這固然安全,但是也實在低效。我們假設(shè)一個進程有1萬個VMA,然后我們在其中的1個VMA上面進行page fault,其他的9999個VMA消失不消失,變化不變化,跟我這個page fault之間其實是沒有半毛錢關(guān)系的。如果能夠在PF中不去持有mmap_lock讀鎖,而去持有一個更細(xì)粒度的,只關(guān)心本VMA的鎖,應(yīng)該是一個更好的選擇。
在處理page fault的時候,我們只需要通過持有VMA的lock,來保證這個VMA本身的穩(wěn)定:
struct vm_area_struct *lock_vma_under_rcu(struct mm_struct *mm, unsigned long address);
它的這個實現(xiàn)看起來很奇怪,因為它拿到了vma->vm_lock->lock后,并不真地會一直拿著,而是馬上就放了up_write,但是它寫了一個vma->vm_lock_seq,把這個vm_lock_seq寫成了vma->vm_mm->mm_lock_seq的,而進程級的mm_lock_seq會在mmap_lock釋放的時候自增。
但是拿讀鎖的page fault,則是在page fault的途中一直hold著vma->vm_lock->lock。lock_vma_under_rcu()會調(diào)用vma_start_read():
因為我們要開始VMA寫的時候把vma->vm_lock_seq寫成了進程級的mm_lock_seq,這樣當(dāng)我們拿讀鎖的時候,如果vma->vm_lock_seq == mm->mm_lock_seq,說明VMA還在寫,我們其實也不用拿讀鎖了,per-VMA讀鎖直接失敗,讓page fault的代碼回退到去拿原先的mmap_lock就好。
由于per-VMA拿寫鎖的人總是當(dāng)場放寫鎖,我們其實就不用擔(dān)心忘記up_write了。這有點自動化的類似后面將要提到的scope-based resource management。
值得一提是,在per-VMA lock準(zhǔn)備好之前,有些Linux內(nèi)核,比如Android采用了SPF(Speculative page faults)來處理page fault,SPF的實現(xiàn)不包含per-VMA lock,它也不拿mmap_sem,但是page fault會不拿mmap_sem投機執(zhí)行,處理過程中會邊走邊看,如果執(zhí)行過程中發(fā)現(xiàn)VMA被修改,page fault會拿mmap_sem來retry原先的page fault。這個機制我們在2022年終盤點中也有提及。
NUMA系統(tǒng)上kernel代碼段復(fù)制
Russell King,在Linux ARM體系架構(gòu)采用device tree之前,維護著ARM Linux社區(qū)。由于當(dāng)時的arch/arm目錄充斥著大量的冗余描述硬件的代碼,在2011年TI OMAP的一次Pull request中,Linus終于忍無可忍,破口大罵“this whole ARM thing is a f*cking pain in the ass”。此后,Linaro和ARM強勢介入,在ARM Linux引入了device tree,開啟了一個嶄新的時代。自己的地盤被人革了命,Russell童鞋的黯然神傷無可掩飾。但是,作為大神,Russell無疑擁有無可辯駁的技術(shù)實力,這次他給我們帶來的是黯然銷魂掌arm64 kernel text replication。
在一個典型的NUMA系統(tǒng)中,跨node訪問內(nèi)存的開銷比訪問本地node的開銷大。
Large folios/動態(tài)大頁
Large folios是社區(qū)2023的熱門話題,由于一個large folio中可以包含多個page,所以采用large folio可以減小page fault的次數(shù)(比如一個page fault中映射1個包含16個page的folio,這樣就減少了后面15次page fault)、降低LRU的維護成本(large folio整體加入LRU)、降低內(nèi)存的回收成本(large folio整體回收)等。