• yocto编译内核


    yocto编译内核

    yocto默认规则的BB文件命名规范是由PN PV 和PR三个变量组成,其中PN是recipes名,PV代表版本号,PR代表子版本号。
    比如meta-qt中随便找一个bb文件:
    在这里插入图片描述
    可以看到bb文件名为python3-gsocketpool_0.1.6.bb
    其中python3-gsocketpool同recipe名,0.1.6是版本号名,子版本号不写默认是r1
    在yocto中bb文件的名称是可以通过bitbake命令进行编译的,比如:

    bitbake python3-gsocketpool
    
    • 1

    但是在yocto中编译内核的时候通常使用如下指令:

     bitbake  virtual/kernel
    
    • 1

    在yocto中内核bb文件如下:
    在这里插入图片描述
    正常使用bitbake linux-fsmp1也是可以的。下面分析一下virtual/kernel的由来

    recipes可以覆盖或者添加到可用于指定该包的符号名字的PROVIDES变量。然后依赖于它的包可以引用符号名字。如果多个包或者同一个包的多个版本提供相同的功能性,那么这是有意义的:

    PROVIDES =+ virtual/kernel

    这个用在recipes中针对kernel的声明将增加符号名字virtual/kernel到名字列表。符号名字实际上做什么不重要,但是为了避免符号名字和实际其他菜谱名字的无意冲突,开发者已经采用惯例来使用前缀virtual/用于符号名字 。

    kernel的版本可能随着发展需要会进行升级,这时源码中就会存在多个相同符号名字的bb文件。这时候需要指定使用的具体版本号。
    在yocto中使用PREFERRED_PROVIDER变量来解决这个问题
    比如:

    PREFERRED_PROVIDER_virtual/kernel = "linunx-fsmp1_5.4"
    
    • 1

    这样就会指定系统使用fsmp1_5.4的内核来构建系统,通常这种配置都会在发行版的conf文件中有涉及。

    yocto内核编译分析

    内核编译及产物

    本小节想详细分析一下yocto中内核的编译过程。
    编译内核:

    bitbake virtual/kernel
    
    • 1

    配置内核以后再编译内核:

    bitbake virtual/kernel -c menuconfig
    bitbake virtual/kernel -C compile
    
    • 1
    • 2

    build-openstlinuxeglfs-fsmp1a/tmp-glibc/work/fsmp1a-ostl-linux-gnueabi/linux-fsmp1/5.4.31-r0路径下看到编译输出:
    在这里插入图片描述最终kernel的打包产物在package下面:
    在这里插入图片描述

    内核bb文件详细分析

    下面fsmp1开发板的linux-fsmp1_5.4bb文件,删减部分无关信息:

    SUMMARY = "Linux STM32MP Kernel"
    SECTION = "kernel"
    LICENSE = "GPLv2"
    LIC_FILES_CHKSUM = "file://COPYING;md5=bbea815ee2795b2f4230826c0c6b8814"
    
    include linux-fsmp1.inc
    
    LINUX_VERSION = "5.4"
    LINUX_SUBVERSION = "31"
    SRC_URI = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-${LINUX_VERSION}.${LINUX_SUBVERSION}.tar.xz;name=kernel"
    SRC_URI[kernel.sha256sum] = "a11083f8f809887f6a0f8d4467532385b99418f17998fe6e837807491c276eeb"
    
    SRC_URI += " \
        file://${LINUX_VERSION}/${LINUX_VERSION}.${LINUX_SUBVERSION}/0001-ARM-stm32mp1-r1-MACHINE.patch \
        file://${LINUX_VERSION}/${LINUX_VERSION}.${LINUX_SUBVERSION}/0002-ARM-stm32mp1-r1-CPUFREQ.patch \
        ......
    "
    
    PV = "${LINUX_VERSION}.${LINUX_SUBVERSION}"
    S = "${WORKDIR}/linux-${LINUX_VERSION}.${LINUX_SUBVERSION}"
    
    # ---------------------------------
    # Configure devupstream class usage
    # ---------------------------------
    BBCLASSEXTEND = "devupstream:target"
    
    # SRC_URI_class-devupstream = "git://github.com/STMicroelectronics/linux.git;protocol=https;branch=v${LINUX_VERSION}-stm32mp;name=linux"
    # SRCREV_class-devupstream = "b8663f5fdb5cfd6f243b72c9fac82c24b2594294"
    SRCREV_FORMAT_class-devupstream = "linux"
    PV_class-devupstream = "${LINUX_VERSION}+github+${SRCPV}"
    
    # ---------------------------------
    # Configure default preference to manage dynamic selection between tarball and github
    # ---------------------------------
    STM32MP_SOURCE_SELECTION ?= "tarball"
    
    DEFAULT_PREFERENCE = "${@bb.utils.contains('STM32MP_SOURCE_SELECTION', 'github', '-1', '1', d)}"
    
    # ---------------------------------
    # Configure archiver use
    # ---------------------------------
    include ${@oe.utils.ifelse(d.getVar('ST_ARCHIVER_ENABLE') == '1', 'linux-fsmp1-archiver.inc','')}
    
    # -------------------------------------------------------------
    # Defconfig
    #
    KERNEL_DEFCONFIG        = "stm32_fsmp1a_defconfig"
    FSMP1A_USB_SERIAL		= "fsmp1a"
    
    KERNEL_CONFIG_FRAGMENTS = "${@bb.utils.contains('KERNEL_DEFCONFIG',	'stm32_fsmp1a_defconfig', '${S}/arch/arm/configs/fragment-01-multiv7_cleanup.config', '', d)}"
    KERNEL_CONFIG_FRAGMENTS += "${@bb.utils.contains('KERNEL_DEFCONFIG', 'stm32_fsmp1a_defconfig', '${S}/arch/arm/configs/fragment-02-multiv7_addons.config', '', d)}"
    .......
    
    KERNEL_CONFIG_FRAGMENTS += "${@bb.utils.contains('DISTRO_FEATURES', 'systemd', '${WORKDIR}/fragments/5.4/fragment-03-systemd.config', '', d)} "
    KERNEL_CONFIG_FRAGMENTS += "${@bb.utils.contains('COMBINED_FEATURES', 'optee', '${WORKDIR}/fragments/5.4/fragment-04-optee.config', '', d)}"
    KERNEL_CONFIG_FRAGMENTS += "${WORKDIR}/fragments/5.4/fragment-05-modules.config"
    KERNEL_CONFIG_FRAGMENTS += "${@oe.utils.ifelse(d.getVar('KERNEL_SIGN_ENABLE') == '1', '${WORKDIR}/fragments/5.4/fragment-06-signature.config','')} "
    
    SRC_URI += "file://${LINUX_VERSION}/fragment-03-systemd.config;subdir=fragments"
    SRC_URI += "file://${LINUX_VERSION}/fragment-04-optee.config;subdir=fragments"
    ......
    
    # -------------------------------------------------------------
    # Kernel Args
    #
    KERNEL_EXTRA_ARGS += "LOADADDR=${ST_KERNEL_LOADADDR}"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    重要变量介绍:
    LIC_FILES_CHKSUM:许可文件的名字和MD5校验和。
    LINUX_VERSION: 正在构建的Linux内核的版本号
    SRC_URI: 指定到Linux内核tar包的路径。另外,变量必须指定一个包含内核配置的defconfig文件。
    SRC_URI[md5sum],SRC_URI[sha256sum]: 用于远程下载的校验和
    S:内核源被解压到的目录。
    COMPATIBLE_MACHINE:被内核支持的机器的名字列表。
    KERNEL_FEATURES:包含内核特性配置的文件列表。
    KMACHINE:Linux内核已知的硬件机器的名字。每个内核菜谱都必须设置这个变量。

    从linux-fsmp1_5.4bb文件中可以看到主要干了几件事:

    1. 指定内核的版本号、校验和、下载路径
    2. 添加内核的patch 和 config片段到SRC_URI变量
    3. 指定了一个内核启动参数,即内核的链接地址为${ST_KERNEL_LOADADDR}
    4. 包含 了两个引用文件linux-fsmp1.inc 和linux-fsmp1-archiver.inc

    可以看到实际的定制细节没有在linux-fsmp1_5.4bb中体现,linux-fsmp1-archiver.inc里面并没有干什么事,重头戏都在linux-fsmp1.inc里面。下面重点关注这个文件内容:

    COMPATIBLE_MACHINE = "(stm32mpcommon)"
    inherit kernel
    DEPENDS += "openssl-native util-linux-native libyaml-native"
    B = "${WORKDIR}/build"
    # Configure build dir for externalsrc class usage through devtool
    EXTERNALSRC_BUILD_pn-${PN} = "${WORKDIR}/build"
    
    # To share config fragments between layers
    FILESEXTRAPATHS_prepend := "${THISDIR}:"
    
    # -------------------------------------------------------------
    # Do not deploy kernel module with specfic tarball
    MODULE_TARBALL_DEPLOY = "0"
    do_deploy[sstate-outputdirs] = "${DEPLOY_DIR_IMAGE}/kernel"
    
    #---------------------------------------------------------------
    # Module kernel signature
    KERNEL_SIGN_ENABLE ?= "0"
    EXTRA_OEMAKE += "${@oe.utils.ifelse(d.getVar('KERNEL_SIGN_ENABLE') == '1', 'INSTALL_MOD_STRIP=1','')}"
    
    #
    # Force task order for link creation
    #
    python () {
        d.appendVarFlag("do_unpack", "postfuncs", " do_symlink_kernsrc")
    }
    do_symlink_kernsrc[noexec] = "1"
    
    
    # ---------------------------------------------------------------------
    # Defconfig
    #
    #If the defconfig is contained on the kernel tree (arch/${ARCH}/configs)
    #you must use the following line
    do_configure_prepend() {
        unset CFLAGS CPPFLAGS CXXFLAGS LDFLAGS MACHINE
        if [ ! -z ${KERNEL_DEFCONFIG} ];
        then
            bbnote "Kernel customized: configuration of linux STM by using DEFCONFIG: ${KERNEL_DEFCONFIG}"
            oe_runmake ${PARALLEL_MAKE} -C ${S} O=${B} CC="${KERNEL_CC}" LD="${KERNEL_LD}" ${KERNEL_DEFCONFIG}
        else
            if [ ! -z ${KERNEL_EXTERNAL_DEFCONFIG} ];
            then
                bbnote "Kernel customized: configuration of linux STM by using external DEFCONFIG"
                install -m 0644 ${WORKDIR}/${KERNEL_EXTERNAL_DEFCONFIG} ${B}/.config
                oe_runmake -C ${S} O=${B} CC="${KERNEL_CC}" LD="${KERNEL_LD}" oldconfig
            else
                bbwarn "You must specify KERNEL_DEFCONFIG or KERNEL_EXTERNAL_DEFCONFIG"
                die "NO DEFCONFIG SPECIFIED"
            fi
        fi
    
        if [ ! -z "${KERNEL_CONFIG_FRAGMENTS}" ]
        then
            for f in ${KERNEL_CONFIG_FRAGMENTS}
            do
                bbnote "file = $f"
                # Check if the config fragment was copied into the WORKDIR from
                # the OE meta data
                if [ ! -e "$f" ]
                then
                    echo "Could not find kernel config fragment $f"
                    exit 1
                fi
            done
    
            bbnote "${S}/scripts/kconfig/merge_config.sh -m -r -O ${B} ${B}/.config ${KERNEL_CONFIG_FRAGMENTS} 1>&2"
            # Now that all the fragments are located merge them.
            (${S}/scripts/kconfig/merge_config.sh -m -r -O ${B} ${B}/.config ${KERNEL_CONFIG_FRAGMENTS} 1>&2 )
        fi
    
        yes '' | oe_runmake -C ${S} O=${B} CC="${KERNEL_CC}" LD="${KERNEL_LD}" oldconfig
        #oe_runmake -C ${S} O=${B} savedefconfig && cp ${B}/defconfig ${WORKDIR}/defconfig.saved
    }
    
    # ---------------------------------------------------------------------
    do_install_append() {
        # Install KERNEL_IMAGETYPE for kernel-imagebootfs package
        install -m 0644 ${KERNEL_OUTPUT_DIR}/${KERNEL_IMAGETYPE} ${D}/${KERNEL_IMAGEDEST}
    
        if ${@bb.utils.contains('MACHINE_FEATURES','gpu','true','false',d)}; then
            # when ACCEPT_EULA are filled
            echo "blacklist etnaviv" > ${D}/${sysconfdir}/modprobe.d/blacklist.conf
        fi
    }
    
    # ---------------------------------------------------------------------
    # Support checking the kernel load address parameter: expecting proper value for ST kernel.
    #
    python do_loadaddrcheck() {
        if not d.getVar('ST_KERNEL_LOADADDR'):
            bb.fatal('Missing ST_KERNEL_LOADADDR value for ST kernel build: please define it in your machine.')
    }
    
    PACKAGES =+ "${KERNEL_PACKAGE_NAME}-headers ${KERNEL_PACKAGE_NAME}-imagebootfs"
    FILES_${KERNEL_PACKAGE_NAME}-headers = "${exec_prefix}/src/linux*"
    FILES_${KERNEL_PACKAGE_NAME}-image  += "boot/ ${KERNEL_IMAGEDEST}"
    FILES_${KERNEL_PACKAGE_NAME}-imagebootfs = "boot/*.dtb boot/${KERNEL_IMAGETYPE}"
    FILES_${KERNEL_PACKAGE_NAME}-modules += "${sysconfdir}/modprobe.d"
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100

    重要内容解析:

    COMPATIBLE_MACHINE:指定了内核支持的机器的名字是 stm32mpcommon
    inherit kernel :引用了OE的kernel类,帮我们作绝大多数的编译内核工作
    DEPENDS : 指定内核的依赖包
    B:指定内核编译的目录
    FILESEXTRAPATHS_prepend:将本菜谱的路径添加到构建系统的搜索路径
    do_deploy[sstate-outputdirs]: 指定内核部署的构建输出目录,最终的路径在tmp-glibc/deploy/images/fsmp1a/kernel下
    实际编译以后的构建输出如下:
    在这里插入图片描述

    EXTRA_OEMAKE += "${@oe.utils.ifelse(d.getVar('KERNEL_SIGN_ENABLE') == '1', 'INSTALL_MOD_STRIP=1','')}"  
    
    • 1

    指定编译内核的配置选项,INSTALL_MOD_STRIP表示是否去掉module的符号等调试选项,减小module的占用内存大小

    d.appendVarFlag("do_unpack", "postfuncs", " do_symlink_kernsrc"):
    
    • 1

    yocto中可以在任务的主函数之前和之后运行函数。这是使用任务的“prefuncs”和“postfuncs”标志来列出要运行的函数完成的。上面的意思就是在do_unpack 解压内核以后运行symlink_kernsrc。

    do_symlink_kernsrc[noexec] = "1" :
    
    • 1

    强制每次编译kernel都作一次symlink_kernsrc动作,noexec表示不关注共享缓存状态,强制执行

    do_configure_prepend():
    
    • 1

    do_configure_prepend函数表示在作kernel的配置之前先运行这段函数,这段函数的大致流程如下:

    1. 如果指定了${KERNEL_DEFCONFIG}或者 ${KERNEL_EXTERNAL_DEFCONFIG},则使用指定的默认配置配置内核
    2. 如果指定了${KERNEL_CONFIG_FRAGMENTS}配置片段,则将所有的配置片段进行合并然后使用合并后的配置作为.config配置内核
      在linux-fsmp1_5.4bb中指定了 KERNEL_DEFCONFIG 和KERNEL_CONFIG_FRAGMENTS ,所以实际配置内核会使用与arch对应的multi_v7_defconfig配置加上linux-fsmp1_5.4bb中指定的所有配置片段合并后的配置文件作为内核的配置。
    do_install_append():
    
    • 1

    第一部分:

    install -m 0644 ${KERNEL_OUTPUT_DIR}/${KERNEL_IMAGETYPE} ${D}/${KERNEL_IMAGEDEST}
    
    • 1

    通过bitbake virtual/kernel -e 查看环境变量可以知道这几个变量的含义分别是:
    KERNEL_OUTPUT_DIR:arch/arm/boot
    KERNEL_IMAGETYPE: zImage vmlinux Image uImage
    KERNEL_IMAGEDEST:boot

    所以该函数的意思是在执行完install流程以后将arch/arm/boot下的 zImage vmlinux Image uImage 都安装到安装目录image/boot下面。
    第二部分:
    如果MACHINE_FEATURES指定了gpu选项则配置blacklist etnaviv 添加到模块自动加载目录

    do_loadaddrcheck():该函数检查bb文件是否指定了内核的链接地址

    PACKAGES =+ "${KERNEL_PACKAGE_NAME}-headers ${KERNEL_PACKAGE_NAME}-imagebootfs":
    
    • 1

    在打包过程中创建 -headers 和-imagebootfs两个包

    FILES_${KERNEL_PACKAGE_NAME}-headers = "${exec_prefix}/src/linux*"
    FILES_${KERNEL_PACKAGE_NAME}-image  += "boot/ ${KERNEL_IMAGEDEST}"
    FILES_${KERNEL_PACKAGE_NAME}-imagebootfs = "boot/*.dtb boot/${KERNEL_IMAGETYPE}"
    FILES_${KERNEL_PACKAGE_NAME}-modules += "${sysconfdir}/modprobe.d"
    
    • 1
    • 2
    • 3
    • 4

    指定被放进特定包中的目录和文件的列表,下面是所有被打包的内容:
    在这里插入图片描述headers 表示将内核的所有头文件放到/usr/src/linux-*下面

    分析Done

  • 相关阅读:
    FDCAN硬件过滤器详解
    flask-cache使用报错Python3 ModuleNotFoundError: No module named ‘werkzeug.contrib‘
    分享一个500页面给大家
    Go 语言中的 Cond 机制详解
    实现一个简单的 ctrl+ f 搜索
    常见逻辑漏洞总结
    百闻不如一见,4款名不见经传的极品软件,让你眼界大开
    12月1日(第三天)
    Scratch软件编程等级考试四级——20210626
    如何在Odoo中添加水印?
  • 原文地址:https://blog.csdn.net/qq_24622489/article/details/127760599