概述
在梳理openharmony對linux內核做了哪些更改時,簡單梳理了下kernel部分的編譯構建流程,并根據源碼做了簡單論證。分享出來,希望對大家有所幫助。
系統版本:openharmony5.0.0
開發板:dayu200
編譯環境:ubuntu22
執行流程
在kernel\linux\build\目錄可以梳理出內核代碼的如下執行流程:
其中BUILD.gn為openharmony中gn+ninja的正常編譯流程,調用過程不再跟蹤。
BUILD.gn
由執行流程圖可知BUILD.gn是通過如下命令進行build_kernel.sh腳本的調用的。
action("build_kernel") {//定義一個構建任務,action參數用于定義構建過程中的具體操作script = "build_kernel.sh"//指定了用于構建內核的腳本文件為`build_kernel.sh`sources = [ kernel_source_dir ]//指定內核源碼目錄//kernel/linux/$linux_kernel_versiondeps = [ ":check_build" ]//在執行構建內核任務之前,必須先完成`:check_build`任務,check_build為上文定義的check_build.sh腳本product_path = "vendor/$product_company/$product_name"//產品自定義相關的目錄build_type = "standard"//構建類型為標準構建outputs = [ "$root_build_dir/packages/phone/images/$kernel_image" ]//構建的最終輸出文件的位置和名稱args = [//列出了傳遞給構建腳本的參數rebase_path(kernel_build_script_dir, root_build_dir),//1.重新計算內核構建腳本目錄相對于構建目錄根路徑的路徑,kernel_build_script_dir = "//kernel/linux/build";,定位到out輸出的目錄rebase_path("$root_out_dir/../KERNEL_OBJ"),//2.重新計算內核對象文件目錄相對于某個輸出目錄的路徑rebase_path("$root_build_dir/packages/phone/images"),//3.重新計算內核鏡像文件放置目錄相對于構建目錄根路徑的路徑build_type,//4.構建類型,這里已經定義為"standard"target_cpu,//5.目標CPU架構,構建內核時需要指定針對哪種CPU架構進行構建product_path,//6.產品路徑,已在前面定義device_name,//7.設備名稱,表示正在構建內核的具體設備型號linux_kernel_version,//8.Linux內核版本號,表示正在構建的內核的具體版本]}
build_kernel.sh
build_kernel.sh腳本主要進行了kernel_module_build.sh腳本的調用。
pushd ${1} #進入編譯目錄即:
./kernel_module_build.sh ${2} ${4} ${5} ${6} ${7} ${8}
.....對編譯文件的復制操作,此處省略
kernel_module_build.sh
此文件主要是將對應的變量做對應的賦值,下面將DAYU200開發板中的編譯方法(./build.sh --product-name rk3568)配置選項按實際情況進行了注釋。
export OUT_DIR=$1 #out
export BUILD_TYPE=$2 #standard
export KERNEL_ARCH=$3 #arm64
export PRODUCT_PATH=$4 #vendor/hihope/rk3568
export DEVICE_NAME=$5 #rk3568
export KERNEL_VERSION=$6#linux-5.10
LINUX_KERNEL_OUT=${OUT_DIR}/kernel/src_tmp/${KERNEL_VERSION}
export OHOS_ROOT_PATH=$(pwd)/../../..
....
make -f kernel.mk
由上可見最終在執行make時使用-f參數指定了kernel.mk文件。
kernel.mk
為了簡化說明下面將DAYU200(rk3568)開發板相關的內容保留之后的makefile文件保留了,如下:
PRODUCT_NAME=$(TARGET_PRODUCT) #產品的名稱,取自環境變量`TARGET_PRODUCT
OHOS_BUILD_HOME := $(realpath $(shell pwd)/../../../) #構建系統的主目錄,通過`realpath`和`pwd`命令計算出絕對路徑#
KERNEL_SRC_TMP_PATH := $(OUT_DIR)/kernel/${KERNEL_VERSION} #內核源代碼的臨時路徑
KERNEL_OBJ_TMP_PATH := $(OUT_DIR)/kernel/OBJ/${KERNEL_VERSION} #內核編譯對象的臨時路徑#如果`BUILD_TYPE`為`standard`,則設置`BOOT_IMAGE_PATH`和`KERNEL_SRC_TMP_PATH`,并導出`KERNEL_SRC_DIR`環境變量
ifeq ($(BUILD_TYPE), standard)BOOT_IMAGE_PATH = $(OHOS_BUILD_HOME)/device/board/hisilicon/hispark_taurus/uboot/prebuiltsKERNEL_SRC_TMP_PATH := $(OUT_DIR)/kernel/src_tmp/${KERNEL_VERSION}export KERNEL_SRC_DIR=out/KERNEL_OBJ/kernel/src_tmp/${KERNEL_VERSION}
endifKERNEL_SRC_PATH := $(OHOS_BUILD_HOME)/kernel/linux/${KERNEL_VERSION}#內核源代碼的實際路徑
KERNEL_PATCH_PATH := $(OHOS_BUILD_HOME)/kernel/linux/patches/${KERNEL_VERSION}#內核補丁文件的路徑
KERNEL_CONFIG_PATH := $(OHOS_BUILD_HOME)/kernel/linux/config/${KERNEL_VERSION}#內核配置文件的路徑
PREBUILTS_GCC_DIR := $(OHOS_BUILD_HOME)/prebuilts/gcc#預編譯的GCC工具鏈路徑
CLANG_HOST_TOOLCHAIN := $(OHOS_BUILD_HOME)/prebuilts/clang/ohos/linux-x86_64/llvm/bin#預編譯的Clang工具鏈路徑#
KERNEL_HOSTCC := $(CLANG_HOST_TOOLCHAIN)/clang #用于主機編譯的工具鏈
KERNEL_PREBUILT_MAKE := make #使用的`make`工具
CLANG_CC := $(CLANG_HOST_TOOLCHAIN)/clang #交叉編譯工具鏈的前綴,初始為空KERNEL_CROSS_COMPILE :=
......KERNEL_TARGET_TOOLCHAIN := $(PREBUILTS_GCC_DIR)/linux-x86/aarch64/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/binKERNEL_TARGET_TOOLCHAIN_PREFIX := $(KERNEL_TARGET_TOOLCHAIN)/aarch64-linux-gnu-#選擇相應的交叉編譯工具鏈和前綴
.....KERNEL_CROSS_COMPILE += CC="$(CLANG_CC)"
KERNEL_CROSS_COMPILE += CROSS_COMPILE="$(KERNEL_TARGET_TOOLCHAIN_PREFIX)"
KERNEL_MAKE := \PATH="$(BOOT_IMAGE_PATH):$$PATH" \$(KERNEL_PREBUILT_MAKE) #設置`PATH`環境變量并使用`make`命令DEVICE_PATCH_DIR := $(OHOS_BUILD_HOME)/kernel/linux/patches/${KERNEL_VERSION}/$(DEVICE_NAME)_patch
DEVICE_PATCH_FILE := $(DEVICE_PATCH_DIR)/$(DEVICE_NAME).patch
KERNEL_IMAGE_FILE := $(KERNEL_SRC_TMP_PATH)/arch/$(KERNEL_ARCH)/boot/$(KERNEL_IMAGE)#生成的內核鏡像文件路徑
DEFCONFIG_FILE := $(DEVICE_NAME)_$(BUILD_TYPE)_defconfig#內核配置文件名
UNIFIED_COLLECTION_PATCH_FILE := ${OHOS_BUILD_HOME}/kernel/linux/common_modules/ucollection/apply_ucollection.sh#統一集合補丁腳本路徑#export KBUILD_OUTPUT=$(KERNEL_OBJ_TMP_PATH)#導出`KBUILD_OUTPUT`環境變量,指定內核編譯輸出路徑$(KERNEL_IMAGE_FILE):$(hide) echo "build kernel..."
......$(hide) rm -rf $(KERNEL_SRC_TMP_PATH);mkdir -p $(KERNEL_SRC_TMP_PATH);cp -arfL $(KERNEL_SRC_PATH)/* $(KERNEL_SRC_TMP_PATH)/ # 復制源代碼$(hide) $(OHOS_BUILD_HOME)/drivers/hdf_core/adapter/khdf/linux/patch_hdf.sh $(OHOS_BUILD_HOME) $(KERNEL_SRC_TMP_PATH) $(KERNEL_PATCH_PATH) $(DEVICE_NAME) # 調用`patch_hdf.sh`腳本,應用設備驅動框架相關的補丁
......$(hide) cd $(KERNEL_SRC_TMP_PATH) && test -f $(DEVICE_PATCH_FILE) && patch -p1 < $(DEVICE_PATCH_FILE) || true #根據產品路徑或設備名稱,應用特定的補丁文件ifeq ($(UNIFIED_COLLECTION_PATCH_FILE), $(wildcard $(UNIFIED_COLLECTION_PATCH_FILE)))#如果存在獲取進程cpu維測數據的腳本(提升獲取CPU使用率的效率的服務),則執行該腳本,此腳本通過創建符號鏈接,可以方便地將位于不同目錄的源碼文件鏈接到內核構建目錄中,避免了復制文件的過程$(hide) $(UNIFIED_COLLECTION_PATCH_FILE) $(OHOS_BUILD_HOME) $(KERNEL_SRC_TMP_PATH) $(DEVICE_NAME) $(KERNEL_VERSION)
endif#復制內核配置文件到源代碼臨時路徑。使用`make`命令進行清理、配置、模塊準備和編譯。$(hide) cp -rf $(KERNEL_CONFIG_PATH)/. $(KERNEL_SRC_TMP_PATH)/$(hide) $(KERNEL_MAKE) -C $(KERNEL_SRC_TMP_PATH) ARCH=$(KERNEL_ARCH) $(KERNEL_CROSS_COMPILE) distclean #清理$(hide) $(KERNEL_MAKE) -C $(KERNEL_SRC_TMP_PATH) ARCH=$(KERNEL_ARCH) $(KERNEL_CROSS_COMPILE) $(DEFCONFIG_FILE) #配置
ifeq ($(KERNEL_VERSION), linux-5.10)$(hide) $(KERNEL_MAKE) -C $(KERNEL_SRC_TMP_PATH) ARCH=$(KERNEL_ARCH) $(KERNEL_CROSS_COMPILE) modules_prepare #模塊準備
endif$(hide) $(KERNEL_MAKE) -C $(KERNEL_SRC_TMP_PATH) ARCH=$(KERNEL_ARCH) $(KERNEL_CROSS_COMPILE) -j64 $(KERNEL_IMAGE) #編譯
....
#定義一個偽目標`build-kernel`,依賴于`$(KERNEL_IMAGE_FILE)`。
#當運行`make build-kernel`時,會觸發內核鏡像的構建。
.PHONY: build-kernel
build-kernel: $(KERNEL_IMAGE_FILE)
📑$(hide)
通常用于 make
文件中的命令前綴。它會將命令的輸出重定向到 /dev/null
,從而隱藏命令的輸出。調試時可以在調用 make
時使用 -s
選項來禁用所有命令的輸出隱藏。
通過對以上的分析,可總結出我們比較關注的信息如下:
合入HDF補丁(patch_hdf.sh)
在第47行中可見HDF的補丁合入方法,合入不同內核版本對應的HDF內核補丁:
$(hide) $(OHOS_BUILD_HOME)/drivers/hdf_core/adapter/khdf/linux/patch_hdf.sh $(OHOS_BUILD_HOME) $(KERNEL_SRC_TMP_PATH) $(KERNEL_PATCH_PATH) $(DEVICE_NAME)
patch_hdf.sh腳本四個參數含義為:第一個入參為工程根目錄路徑,第二入參為內核目錄路徑,第三個入參為內核版本路徑,第四個參數是當前設備名。
此文件主要執行將hdf相關的補丁(kernel\linux\pathces\linux-5.10\rk3568_patch\hdf.patch)打入到系統中,具體的操作可以直接參看源碼中的腳本文件drivers\hdf_core\adapter\khdf\linux\patch_hdf.sh
特定補丁文件
在/kernel/linux/patches/${KERNEL_VERSION}/$(DEVICE_NAME)_patch
目錄中合入特定補丁$(DEVICE_NAME).patch
,
$(hide) cd $(KERNEL_SRC_TMP_PATH) && test -f $(DEVICE_PATCH_FILE) && patch -p1 < $(DEVICE_PATCH_FILE) || true
$(hide)
:這是make
工具的一個特性,用于隱藏命令的輸出。它會將命令的輸出重定向到/dev/null
,使得構建日志更加簡潔,只顯示關鍵信息。cd $(KERNEL_SRC_TMP_PATH)
:切換到內核源碼所在的臨時目錄$(KERNEL_SRC_TMP_PATH)
。test -f $(DEVICE_PATCH_FILE)
:檢查設備補丁文件$(DEVICE_PATCH_FILE)
是否存在。如果文件存在,命令返回真(退出碼為 0);如果文件不存在,命令返回假(退出碼為非 0)。patch -p1 < $(DEVICE_PATCH_FILE)
:應用補丁文件$(DEVICE_PATCH_FILE)
。-p1
參數表示剝離補丁路徑中的一層目錄,即假設補丁是相對于內核源碼根目錄的一級目錄創建的。|| true
:如果前面的命令(即test -f
和patch
的組合)執行失敗(返回非 0 退出碼),則執行true
命令,true
命令總是返回真(退出碼為 0)。這可以避免構建過程因為補丁文件不存在或應用補丁失敗而中斷。
在我拿到的這份源碼中沒有rk3568.patch這個補丁,所以true很重要,表示此特定補丁為可選項。
內核編譯配置
在58行中可見如下腳本
$(KERNEL_MAKE) -C $(KERNEL_SRC_TMP_PATH) ARCH=$(KERNEL_ARCH) $(KERNEL_CROSS_COMPILE) $(DEFCONFIG_FILE)
經過上下文的分析可將此腳本解釋為
make -C /out/kernel/src_tmp/linux-5.10 ARCH=arm64 CC=/prebuilts/clang/ohos/linux-x86_64/llvm/bin/clang CROSS_COMPILE=/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu- rk3568_standard_defconfig
-C /out/kernel/src_tmp/linux-5.10
:指定了內核源碼所在的目錄,make
會進入該目錄下進行編譯操作,而不是在當前目錄。
ARCH=arm64
:定義了目標架構為 ARM64,這意味著要編譯出適用于 ARM64 架構設備的內核。CC=/prebuilts/clang/ohos/linux-x86_64/llvm/bin/clang
:指定了 C 編譯器的路徑,這里使用的是 Clang 編譯器,它位于/prebuilts/clang/ohos/linux-x86_64/llvm/bin/
目錄下。
CROSS_COMPILE=/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
:指定了交叉編譯前綴,用于構建目標架構(ARM64)上的可執行文件。這個前綴指向了交叉編譯工具鏈的目錄,aarch64-linux-gnu-
表示該工具鏈是針對aarch64
架構的 Linux 系統的。
rk3568_standard_defconfig
:指定使用的內核配置文件。這個文件定義了內核的各種配置選項,如要包含的驅動、功能模塊等。它會使內核編譯過程按照該配置文件中的設置來進行,生成符合特定硬件平臺(這里是 rk3568)需求的內核配置。
通過對上文的分析發現沒有對kernel進行打補丁,后經搜索發現在device\board\hihope\rk3568\kernel\目錄中包含對kernel的操作,如下
#device\board\hihope\rk3568\kernel\build_kernel.sh
patch -p1 < ${KERNEL_PATCH}#KERNEL_PATCH即為/kernel/linux/patches/linux-5.10/rk3568_patch/kernel.patch
此目錄也包含廠家對系統其他的特殊處理,整體流程與上文分析的類似,此處不再詳細說明。