openharmony5.0.0中kernel子系统编译构建流程概览(rk3568)
概述
在梳理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
此目录也包含厂家对系统其他的特殊处理,整体流程与上文分析的类似,此处不再详细说明。