1. 概述
Alibaba Cloud Linux 2(原
Aliyun Linux 2,簡稱Alinux 2)是阿裡雲作業系統團隊基于開源Linux核心4.19 LTS版本打造的一款針對雲應用場景的下一代Linux OS發行,不僅提供Linux社群的最新增強功能,在提供雲上最佳使用者體驗的同時,也針對阿裡雲基礎設施做了深度的優化。今日
Alinux 2 LTS 正式釋出,是Alinux 2的一個重要裡程碑。這标志着阿裡雲作業系統團隊将為Alinux 2提供長期技術支援、穩定的更新、更好服務,為Alinux 2的使用者提供更多保障。
Alinux 2 LTS 版本不僅增加了更多社群新功能的支援,對系統啟動時間、運作時性能及穩定性都做了許多優化。更詳細的更新優化可參考
釋出記錄,推薦直接上手試用體驗。
Alinux 2在快速啟動優化上取得一些不錯的效果,同時推出“Alinux 2 qboot快速啟動版”鏡像(公測中),核心部分啟動性能提升40%:

這裡分享一下Alinux 2 LTS在系統快速啟動優化上的所做的一些實踐。
2. Linux系統啟動流程
首先定義Linux系統啟動,這裡我們定義為從系統上電(虛拟機開啟),到使用者能夠登陸(ssh login)為系統啟動。通用Linux系統啟動大緻分為三個階段:引導階段(phase#1),核心啟動階段(phase#2)及使用者态啟動階段(phase#3):
其中,
- 對實體機産品,開機後運作固件中的BIOS程式,完成基本硬體初始化及上電自檢(POST),通過後跳轉至系統磁盤引導扇區;
-
對虛拟機(Qemu + KVM)産品,Qemu運作後模拟BIOS,加載系統鏡像檔案虛拟出系統盤,跳轉至系統盤引導扇區;
下面來看看各階段大緻的啟動流程。
2.1 Boot Loader
Bootloader是位于系統引導扇區的一段獨立的系統程式,用于系統啟動初期的硬體初始化,系統分區識别,系統核心加載及跳轉執行。目前應用比較廣泛的bootloader是用于通用系統的grub2和嵌入式系統的uboot。Grub2是多重引導器(multiboot),提供互動界面,預設配置下有5s互動逾時,啟動耗時較長。
2.2 Kernel
Bootloader加載Linux核心(一般為壓縮核心vmlinuz)到記憶體,并運作核心自解壓縮程式,解壓後跳轉至start_kernel,開始核心初始化流程:
2.3 User Space
Linux核心完成一系列初始化動作之後,開始運作init程式,建立PID為1的使用者态程序,将系統控制權從核心态跳轉到使用者态。init程序會繼續進行使用者态啟動流程,開啟各種必要的,或是預先配置的系統服務,最後啟動登陸服務,完成整個系統的啟動。
Initrd與Switch Root
init是使用者态程式,存放在系統根檔案系統(rootfs)裡。核心需要先挂載rootfs,才能運作init程式。通用Linux發行需要支援多種磁盤裝置,多種檔案系統,是以核心必須能夠識别不同的磁盤裝置,不同的檔案系統。這需要核心預加載多種可能的磁盤裝置驅動以及多種檔案系統相關使用者态工具軟體才能正确識别rootfs。而這些驅動及使用者态工具一般都存放在rootfs中,形成一個循環依賴。
為解決這個問題,initrd應運而生,将挂載rootfs必要的驅動,使用者态工具以及其他需要預加載的代碼從rootfs總抽取出來,并依照rootfs的檔案結構,打包成一個小的rootfs,做成一個記憶體盤(ram disk)。核心在挂載最終的rootfs之前,先從記憶體中挂載initrd,加載必要的驅動後,先運作initrd中的init程式,挂載最終的rootfs。然後執行switch root動作,切換至最終的rootfs。
Alinux 2系統采用systemd來管理使用者空間啟動流程,systemd就是init程式,initrd使用壓縮格式的initramfs檔案。是以在加載initrd之前,核心需要先解壓縮initramfs。
Cloud Init
Cloud init是雲環境中的虛拟執行個體初始化配置工具,執行個體啟動階段能從多種資料源讀取相關資料并據此對虛拟機進行配置,如使用者密碼,主機名,網絡,使用者資料等等一些配置。
3. 啟動耗時畫像
優化系統啟動時間,自然需要先對系統啟動畫像,了解啟動時間分布情況,找出系統啟動耗時熱點。
3.1 啟動時間測量
Linux系統有如下常見的啟動時間測量統計方法:
-
systemd-analyze
systemd自帶的啟動分析工具,能夠給出總的啟動時間消耗,已經使用者态服務啟動耗時統計。
-
dmesg
dmesg輸出核心啟動日志,時間戳能夠幫助分析核心初始化各階段耗時情況。配合
選項計算出日志間的時間差,友善快速定位核心啟動過程中耗時熱點。-d
-
initcall_debug
核心啟動參數,開啟後會統計核心各初始化函數的耗時情況,相比
更加精确。dmesg -d
-
printk/trace_printk
要分析一些啟動熱點的細化耗時情況時,手動增加一些printk/trace_printk探針能夠幫助擷取時間統計資訊。
-
ftrace
必要時也可開啟核心早期ftrace功能,幫助分析熱點耗時。不過需要注意開啟ftrace後可能會導緻函數延時增加,是以不宜參考ftrace得出函數絕對耗時,可以參照trace結果幫助分析熱點函數的耗時邏輯。
還有其他一些時間測試方法,以及圖形化畫像工具,這裡不一一介紹。
3.2 啟動耗時熱點分析
對Alinux 2系統啟動畫像後,按耗時排序,得到如下耗時熱點:
(這裡以2C8G虛拟機為例,核心耗時1000ms,總體耗時5000ms)
熱點 | 耗時(ms) | 核心啟動占比 | 總啟動占比 |
---|---|---|---|
mem init | ~35 | 3.5% | 0.7% |
ORC unwind init | ~90 | 9% | 1.8% |
buddy init | 250 | 25% | 5% |
console enable | ~60 | 6% | 1.2 % |
initramfs unpack | |||
free initmem | 270 | 27% | 5.4% |
mouse probe | 650 | 65% | 13% |
systemd initrd | 600 | N/A | 12% |
mount rootfs | 200 | 4% | |
cloud init | 2740 | 54.8% |
可見:
- 總體啟動耗時中,一半以上的時間消耗在使用者态cloud-init程序上;
- 核心啟動階段,滑鼠探測耗時占比較高。
4. 快速啟動優化
4.1 啟動優化方法
常用的啟動優化方法大緻如下:
- 瘦身
- 移除不必要的代碼,如子產品,服務等,縮減啟動初始化步驟;
- 移除不必要的測試,調式及列印
- 精簡共享庫
- 異步、并行
- 将耗時動作從關鍵路徑移除,延後執行
- 将順序動作并行化執行
- 原地執行(XIP)
- 多用于嵌入式系統
- 定制化
- 将通用初始化程式定制化
- 算法優化
- 改進算法,加速初始化時間
4.2 去initrd
從前面的啟動耗時熱點分析結果可以看出,initrd解壓縮及initrd systemd耗時占Alinux 2啟動較大比率。
Alinux 2系統主要面向雲環境虛拟執行個體,系統盤裝置基本固定為virtio-blk裝置,根檔案系統格式基本固定為ext4檔案系統,應該不需要通過initrd來加載rootfs,可以去掉initrd,直接挂載系統磁盤,即對核心啟動瘦身。
理論上會優化掉initramfs unpack(270) + initrd systemd(560) ~ 800ms的啟動耗時。去掉initrd測試結果如下:
可見initrd systemd時間确實優化掉了,但總的啟動時間并沒有理論優化收益。原因是核心啟動耗時增加了約400ms。進一步分析發現,啟動耗時熱點之一的mouse probe(600ms),去initrd之前是與initrd systemd并行執行的。
去掉initrd後,這部分時間就直接計入核心啟動時間了。抵去優化掉的initramfs unpacking的200ms,核心實際增加了400ms左右。
是以,要最大化去initrd的優化收益,必須同時解決mouse probe的耗時。
4.3 延遲probe
通用Linux系統需要支援多種IO裝置,而滑鼠鍵盤是比較常用的輸入裝置,特别是滑鼠,産品繁多,接口多樣。系統啟動過程中加載滑鼠驅動後,需要掃描多種IO總線來探測滑鼠裝置,這一過程非常耗時。
依據前面提到的優化方法,我們有兩種方案:
- 對雲環境定制滑鼠驅動,固定探測virtio裝置;
- 将滑鼠探測從啟動關鍵路徑剝離,延遲探測,與後面系統啟動服務并行;
第一種方案需要重構相關代碼,成本較高;而且定制化限制較多,無法與開源社群協作。是以需要思考第二種方法:延遲探測。一種簡單可行的方法是将原本内置(built-in)的裝置驅動重新編譯為核心子產品(kernel module),因核心子產品存放在根檔案系統,是以加載時機被動推遲到根檔案系統挂載之後,此時核心已經啟動完成,自然與使用者态初始化程序并行執行。
測試結果如下:(注意這是優化後的核心本地測,cloud-init被禁用)
帶initrd啟動:
不帶initrd啟動:
可見,核心啟動時間縮減約200ms,優化掉initrd systemd時間;滑鼠裝置探測延後至userspace初始化階段,導緻userspace啟動時間略有增加。獲得預期的啟動時間優化。
4.4 記憶體初始化優化
記憶體初始化也是核心啟動熱點之一,特别是在大規格執行個體上,記憶體初始化耗時占比較高。圖中為750GB執行個體記憶體初始化耗時:
meminit耗時近2s
buddy init耗時1.8s
記憶體初始化動作是在核心啟動的關鍵路徑上,優化思路是并行初始化。因記憶體初始化時機較早,系統多CPU還未初始化完成,是以需要将記憶體初始化延後至CPU初始化完成之後,采用多線程并行執行記憶體初始化。這部分工作社群已經完成,通過核心配置
CONFIG_DEFERRED_STRUCT_PAGE_INIT
來開啟。
開啟後,記憶體初始化延後,按NUMA node并行執行:
前半部耗時約0.2s
後半部耗時約1.3s
4.5 free initmem修複
Alinux 2 核心啟動優化前有一個機率性的啟動熱點,free initmem到buddy系統時,會大機率(超過70%)出現200ms以上延時,dmesg日志顯示如下,耗時超過200ms:
[ 0.687494] rtc_cmos 00:00: setting system clock to 2020-03-03 15:09:38 UTC (1583248178)
[ 0.915315] Freeing unused kernel image memory: 1836K
經分析,發現是社群已知問題,并在新核心已經
修複于是backport回Alinux 2 LTS核心,修複後耗時約5ms,基本消除這部分延時:
[ 0.482477] rtc_cmos 00:00: setting system clock to 2020-03-03 15:01:41 UTC (1583247701)
[ 0.487438] Freeing unused kernel image memory: 1856K
4.6 ORC unwind初始化
核心中有一些靜态表,需要在核心初始化階段排序,有些表體積較大,初始化耗時占比也不容小觑。如ORC unwind表格初始化排序,耗時約90ms:
[ 0.087330] clocksource: refined-jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 1910969940391419 ns
[ 0.179718] random: get_random_bytes called from start_kernel+0x8b/0x563 with crng_init=0
這些靜态表格是在核心建構階段生成,是以可以将排序動作從核心初始化階段移除,放到核心建構階段完成,以節省核心初始化時間。經調查發現社群已經有類似的優化方案,異常處理表(exception table)排序移植到了核心建構階段完成。于是對異常處理表改進,增加了ORC unwind表格建構階段排序優化,
系列patch已經合入主線。
優化後基本削減了這部分耗時:
[ 0.037253] clocksource: refined-jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 1910969940391419 ns
[ 0.040714] random: get_random_bytes called from start_kernel+0x8b/0x563 with crng_init=0
4.7 Cloud-init優化
Alinux 2采用systemd啟動使用者态系統服務程序,以實作最大化并行啟動。在到達啟動完成的ssh登陸狀态前,依賴一系列必要的系統服務,其中cloud-init是關鍵鍊路中一個耗時熱點,啟動畫像中可以看出cloud-init幾乎占整個系統啟動時間的一半,是以優化cloud-init能夠獲得較大的啟動性能收益。
在cloud-init服務中,一個耗時的配置任務是使用者密碼配置,需要從metadata伺服器擷取賬号密碼,完成配置。Alinux 2 LTS 核心開啟了
Qemu firmware configuration
功能,能夠通過qemu透傳一些諸如賬号密碼的配置到虛拟機内部,使得cloud-init能夠本地讀取配置資訊,加快cloud-init配置動作。感謝阿裡雲鏡像團隊跟阿裡雲虛拟化團隊共同努力,即将推出InnerPasswd功能,加速Alinux 2 LTS 執行個體的cloud-init配置,提升執行個體啟動時間,敬請期待!
4.8 其它優化
另外,啟動階段的console輸出也是一個相對耗時的動作,因為串行口的波特率是固定的,大量是輸出會形成阻塞導緻console enabled延時較大。例如:
開啟console output,console耗時2.6s!:
配置核心參數
quiet
,關閉console output:
5. 下一步工作
雖然Alinux 2 LTS在啟動優化已經取得了不錯的效果,啟動性能得到進一步提升,但仍然還有進一步挖掘的空間。特别是記憶體初始化這塊,仍然是大規格執行個體啟動熱點。即便已經開啟的deferred page init特性,但記憶體初始仍然限于node間并行,而node内并行初始化值得進一步挖掘,特别對目前ECS執行個體大都為單node執行個體(NUMA關閉)的場景下,理論上有更大的收益。
據了解社群已經有貢獻者在着手進行相關工作,值得期待。