首頁  >  文章  >  系統教程  >  Linux 核心裁剪框架初探

Linux 核心裁剪框架初探

王林
王林轉載
2024-02-10 17:30:421230瀏覽

由于操作系统内核的不稳定性、时效性较差、完整性问题以及需要人工干预等原因,Linux内核裁剪技术没有得到广泛的应用。了解了现有技术的局限性,尝试提出一个Linux内核裁剪框架,或许可以解决这些问题。

大约是在2000年的时候,老码农还很年轻,当时希望将Linux 作为手机的操作系统, 于是才有了进行内核裁剪的想法并辅助实践,效果尚好,已经能在PDA上执行手机的功能了。一晃20多年过去了,Linux 已经有了太大的变化,内核裁剪的技术和方式也有了较大的不同。
Linux 的内核裁剪是为了减少目标应用中不需要的内核代码,在安全性和高性能(快速启动时间和减少内存占用)方面有着显著的好处。但是,现有的内核裁剪技术有其局限性,有没有内核裁剪的框架化方法呢?

1. 关于内核裁剪

近年来,Linux操作系统在复杂性和规模上都在增长。然而,一个应用程序通常只需要一部分 OS 功能,众多的应用需求导致了Linux内核的膨胀。操作系统的内核膨胀同样导致了安全性隐患、启动时间变长和内存使用的增加。
随着服务化和微服务的流行,进一步提出了对内核裁剪的需求。在这些场景中,虚拟机运行小型应用程序,每个应用程序往往是“微型”的,内核占用较小,一些虚拟化技术要为目标应用程序提供最简单的 Linux 内核。
鉴于操作系统的复杂性,通过手工挑选内核特性来裁剪内核有些不切实际。例如,Linux 有超过14,000 个配置选项(截至 v4.14) ,每年都会引入数百个新选项。内核配置器(例如 KConfig)只提供用于选择配置选项的用户界面。鉴于糟糕的可用性和文档的不完整性,用户很难选择最小且实用的内核配置。
现有的内核裁剪技术一般遵循三个步骤:

  1. 运行目标应用程序的工作负载并跟踪在应用程序运行期间执行的内核代码;
  2. 分析跟踪并确定目标应用程序所需的内核代码,
  3. 组装一个只包含应用程序所需代码的内核裁剪。

配置驱动的是内核裁剪的一般方法,大多数现有的工具使用配置驱动技术,因为它们是为数不多的可以产生稳定内核的技术之一。配置驱动的内核重载根据功能特性减少了内核代码,配置选项对应于内核的功能,裁剪后的内核只包含用于支持目标应用程序工作负载的功能。
然而,尽管内核裁剪技术在安全性和性能方面非常吸引人,但在实践中并没有得到广泛采用。这并不是因为缺乏需求,实际上,许多云供应商手工编写 Linux 内核来减少代码,但一般不如内核裁剪技术有效。

2. 现有内核裁剪技术的限制

现有内核裁剪技术有五个主要的局限性。

在引导阶段不可见。现有技术只能在内核引导后启动,依赖于 ftrace,因此无法观察在引导阶段加载了哪些内核代码。如果内核中缺少关键模块,内核通常无法启动,而大量的内核功能特性只能通过观察引导阶段来捕获。此外,关于性能和安全性同样只在引导时加载(例如,用于多核支持的 CONFIGSCHEDMC 和 CONFIGSECURITYNETWORK) ,导致了性能和安全性降低。
缺乏对应用程序部署的快速支持。使用现有的工具,面向内核裁剪来部署一个新的应用程序需要完成跟踪、分析和组装这三个步骤。这个过程非常耗时,有可能需要几个小时甚至几天,阻碍了应用部署的敏捷性。
粒度较粗。使用ftrace 只能在函数级跟踪内核代码,粒度太粗,无法跟踪影响函数内代码的配置选项。
覆盖不完全。因为使用动态跟踪,所以需要应用程序工作负载来驱动内核的代码执行,以最大限度地扩大覆盖范围。然而,基准测试覆盖是具有挑战性的,而且,如果应用程序有在跟踪期间没有观察到的内核代码,那么裁剪后的内核可能会在运行时崩溃。
没有区分执行依赖,可能存在冗余。即使实际上可能并不需要执行的代码,也可能包含在了内核功能特性中,例如,可能初始化了第二个文件系统。
前三个限制是可以克服的,可以通过改进设计和工具加以解决,而后两个限制是在所难免,需要在具体的技术之外作出努力。

3. Linux 的内核配置

3.1配置选项

核心配置由一組配置選項組成。一個核心模組可以有多個選項,每個選項都控制哪些程式碼將包含在最終的核心二進位檔案中。
配置選項可控制核心程式碼的不同粒度,例如由 C 預處理器實現的語句和函數,以及基於 Makefile 實現的物件檔案。 C 預處理器根據 #ifdef/#ifndef 選擇程式碼區塊,配置選項用作巨集定義,以決定是否在編譯後的核心中包含這樣條件的程式碼區塊,可以是語句粒度或函數粒度。 Makefile 用來決定是否在編譯後的核心中包含某些物件文件,例如, CONFIG_CACHEFILES 是 Makefile 中的設定選項。
語句級配置選項不能透過現有核心裁切工具所使用的函數級追蹤來識別。事實上,Linux 4.14 中30%左右 的 C 預處理器是語句級選項。
隨著核心程式碼和功能特性的快速成長,核心中的配置選項數量也迅速增加,以 Linux核心3.0以上版本都有1萬多個配置選項。

3.2. 設定語言

Linux核心使用KConfig 設定語言來指示編譯器在編譯後的核心中包含哪些程式碼,允許定義組態選項以及它們之間的依賴關係。
KConfig 中配置選項的值可能是 bool、 tristate 或 constant。 bool 意味著程式碼要么被靜態編譯成內核二進位文件,要么被排除在外,而 tristate 允許程式碼被編譯成一個可載入核心模組,即一個可以在執行時間載入的獨立物件。 constant可以為內核程式碼變數提供字串或數值。一個選項可以依賴另一個選項,KConfig 使用了一個遞歸過程,透過遞歸選擇和取消依賴項。最終的核心配置具有有效的依賴關係,但可能與使用者輸入不同。

3.3. 設定模板

Linux 核心附帶了許多手工製作的設定模板。但是,由於配置模板的硬編碼特性並且需要人工幹預,它們不能適應不同的硬體平台,也不了解應用程式的需求。例如,由 tinyconfig 建構的核心不能在標準硬體上啟動,更不用說支援其他應用程式了。有些工具將localmodconfig 視為最小化的配置,但是,localmodconfig 與靜態配置模板具有相同的局限性,它不會啟動控制語句級或函數級C 預處理器的配置選項,也不會處理可加載的內核模組。
kvmconfig 和 xenconfig 模板是為在 KVM 和 Xen 上運行的核心而客製化的。它們提供例如底層虛擬化和硬體環境的領域知識。

3.4. 雲端中的 Linux 核心配置

#Linux 是雲端服務中占主導地位的作業系統內核,雲端供應商都在一定程度上放棄了普通的 Linux 核心。雲端廠商的客製化通常是透過直接刪除可載入的核心模組來完成的,手工修剪核心模組二進位的問題是可能會違反依賴關係。重要的是,基於應用程式需求可以進一步裁剪內核。例如,Amazon FireCracker 核心是一個專門用於函數即服務的微型虛擬機,使用 HTTPD 作為目標應用程序,在保證功能和效能提升的同時,使核心裁剪實現了更大程度的最小化。

4. 核心裁切的思考

針對局限一,是否可以使用來自 QEMU 的指令級追蹤來實現引導階段的可見性呢?這樣,就可以追蹤內核程式碼並將其映射到內核配置選項。既然引導階段對於產生可引導內核至關重要,使用 hypervisor 提供的追蹤特性來獲得端到端的可觀察性並產生穩定的內核。

針對局限二,根據在NLP深度學習中的經驗,可以使用離線和在線結合的方法,給定一組目標應用程序,可以直接離線生成的App 配置,再和基線配置組合成完整的內核配置,從而產生一個裁剪後的核心。這種可組合性能夠透過重複使用應用程式配置和先前建構的檔案(例如核心模組)來增量地建構新核心。如果目標應用程式的配置已知,就可以在幾十秒內完成核心裁剪。

針對局限三,使用指令級追蹤可以解決控制函數內部功能特性的核心配置選項,指令級追蹤的開銷對於運行測試套件和效能基準來說是可以接受的。

針對局限四,使用基於動態追蹤的一個基本限制是測試套件和基準的不完善,許多開源應用程式測試套件的程式碼覆蓋率較低。組合不同的工作負載來驅動應用程式可以在一定程度上減輕這種限制。

針對局限五,透過刪除在基線核心中執行但在實際部署運行時不需要的核心模組,可以使用特定於領域的資訊進一步載入核心。以 Xen 和 KVM 為例,可以基於 xenconfig 和 kvmconfig 配置模板進一步減少核心大小。面向應用程式的內核裁剪可以進一步減少內核大小甚至廣泛地自訂的內核程式碼。

5 核心裁切框架初探

核心裁切框架的原理沒有改變,仍然是追蹤目標應用程式工作負載的核心佔用情況,以確定所需的核心選項。

5.1 核心裁切框架的核心特性

核心裁切框架大概可以具備以下特性:
端到端的可見性。利用虛擬機器監控程式的可見性來實現端到端的觀察,可以追蹤核心引導階段和應用程式工作負載,可以嘗試在QEMU 的基礎上建造Linux核心的裁剪框架。
可組合性。一個核心思想是透過將內核配置劃分為若干組配置集,使內核配置可以組合,用於在給定的部署環境上引導內核,也可以用於目標應用程式所需的配置選項。配置集分為兩種:基線配置和應用配置。基線配置不一定是在特定硬體上引導所需的最小配置集,而是在引導階段追蹤的一組配置選項。基線配置可以與一個或多個應用程式配置組合在一起,以產生最終的核心配置。
可重用性。基線配置和應用程式配置都可以儲存在資料庫中,並且只要部署環境和應用程式的二進位檔案不變就可以重複使用。這種可重複使用性避免了重複追蹤工作負載的運行,使得配置集的創建成為一次性的工作。
支援快速應用部署。給定一個部署環境和目標應用程序,內核裁剪框架可以有效地檢索基線配置和 應用配置,並將它們組合成所需的內核配置,然後使用生成的配置構建廢棄的內核。
細粒度配置跟踪,基於程式計數器的追蹤來識別基於低階代碼模式的配置選項。

5.2 核心裁切框架的體系結構

核心裁切框架應該同時具備離/線上系統,體系結構如下圖所示:
Linux 内核裁剪框架初探
透過離線系統, 配置追蹤器用於追蹤部署環境和應用程式所需的配置選項,並記錄下來。配置產生器將這些選項處理成基準配置和應用程式配置選項,並將它們儲存在配置資料庫中。
透過線上系統,配置組合器使用基線配置和應用配置來產生目標核心配置,然後,核心建構器產生裁剪後的Linux核心.

5.3 核心裁切框架的實作可行性

配置追蹤
核心裁切框架的配置追蹤器在目標應用程式驅動的核心執行期間追蹤配置選項,使用 PC 暫存器捕獲正在執行的指令的位址。為了確保被追蹤的PC 屬於目標應用程序,而不是其他進程(例如,後台服務) ,可以使用了一個定制的init 腳本,該腳本不啟動任何其他應用程序,只掛載檔案系統/tmp、/proc和/sys ,啟用網路介面(lo 和eth0) ,最後在核心引導後直接啟動應用程式。
同時,可能需要停用核心地址空間配置隨機載入 ,以便能夠正確地將位址對應到原始碼,但在裁剪後的核心中仍然可以使用。然後,將 PC 對應到原始碼語句。可載入的核心模組需要額外的處理,可以使用/proc/module 取得每個載入的核心模組的起始位址,將這些 PC 對應到核心模組二進位中的語句。另一種方法是利用 localmodconfig,但是,localmodconfig 只提供模組粒度等級的資訊。
最後,將語句歸屬於配置。對於基於 C 預處理器的模式 ,分析 C 原始檔以提取預處理器指令,然後檢查這些指令中的語句是否被執行。對於基於 Makefile 的模式 ,決定是否應該在物件檔案的粒度上選擇配置選項。例如,如果使用了任何對應的檔案(bind.o、 achefiles.o 或 daemon.o) ,則需要選擇 CONFIG_CACHEFILES。
配置產生
基線配置和應用配置是在離線系統中產生的。如何判斷啟動階段結束呢?可以使用 mmap 將一個空的存根函數映射到一個預定義地址段,上述的初始化腳本在運行目標應用程式之前調用調用存根函數,因此,可能根據 PC 追蹤中的預定義地址來識別引導階段的結束。
核心裁剪框架從應用程式中取得配置選項,並過濾掉在引導階段觀察到的與硬體相關的選項。這些硬體特性是根據它們在核心原始碼中的位置定義的。不排除這樣的可能性,即與硬體相關的選項只能在應用程式執行期間​​觀察到,例如,它根據需要載入新的裝置驅動程式。
配置組裝
將基線配置與一個或多個應用程式配置組合在一起,可以以產生用於建立核心的最終配置。首先,將所有 配置選項併入一個初始配置,然後使用SAT求解器解決它們之間的依賴關係。嘗試將配置依賴性建模為一個布林可滿足性問題,有效配置是指滿足配置選項之間所有指定依賴性的配置。因為 KConfig 不確保包含所有選定的選項,而是取消選擇未滿足的依賴項,所以才要基於 SAT 求解器對核心配置進行建模。
核心建構
使用於Linux的KBuild基於組裝後的配置選項構建裁剪內核,利用現代make的增量構建可以優化構建時間,也可以緩存以前的構建結果(例如,目標文件和內核模組) ,以避免冗餘的編譯和連結。當發生配置變更時,只有對配置選項進行變更的模組重新構建,而其他檔案可以重複使用。

6. 小結

#由於作業系統核心的不穩定性、時效性較差、完整性問題以及需要人工幹預等原因,Linux核心裁剪技術並沒有得到廣泛的應用。了解了現有技術的局限性,並嘗試提出Linux核心裁剪框架,或許可以解決這些問題。

以上是Linux 核心裁剪框架初探的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:lxlinux.net。如有侵權,請聯絡admin@php.cn刪除