Ubuntu 24.04交叉编译Linux内核每次全量重编译问题排查
Ubuntu 24.04交叉编译Linux内核时全量重编译的原因分析
问题背景
在Ubuntu 20.04和Ubuntu 24.04上使用同一代码仓库交叉编译ARM64 Linux内核,执行命令:
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- Image modules -j4
两个系统均使用amd64 gcc 9.5及同一交叉编译工具链,但Ubuntu 24.04每次执行编译都会全量重编译所有内容,而Ubuntu 20.04保持正常增量编译,且所有文件时间戳未发生变化,仅系统版本不同。
Ubuntu 24.04编译输出片段:
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- Image modules -j4 CHK include/config/kernel.release CHK include/generated/uapi/linux/version.h CC scripts/mod/empty.o CC scripts/mod/devicetable-offsets.s MKELF scripts/mod/elfconfig.h HOSTCC scripts/mod/modpost.o HOSTCC scripts/mod/sumversion.o GEN scripts/mod/devicetable-offsets.h HOSTCC scripts/mod/file2alias.o CHK include/generated/utsrelease.h CHK include/generated/timeconst.h CC kernel/bounds.s CHK include/generated/bounds.h CC arch/arm64/kernel/asm-offsets.s CHK include/generated/asm-offsets.h CALL scripts/checksyscalls.sh HOSTLD scripts/mod/modpost CC init/main.o ....
排查细节
使用-j1和V=1参数对比两个系统的编译输出,发现Ubuntu 24.04存在以下关键差异:
- 编译初期多执行了一系列临时文件生成与清理步骤:
set -e; : ' CHK include/config/kernel.release'; mkdir -p include/config/; echo "4.4.71$(/bin/bash ./scripts/setlocalversion .)" < include/config/auto.conf > include/config/kernel.release.tmp; if [ -r include/config/kernel.release ] && cmp -s include/config/kernel.release include/config/kernel.release.tmp; then rm -f include/config/kernel.release.tmp; else : ' UPD include/config/kernel.release'; mv -f include/config/kernel.release.tmp include/config/kernel.release; fi make -f ./scripts/Makefile.asm-generic \ src=asm obj=arch/arm64/include/generated/asm make -f ./scripts/Makefile.asm-generic \ src=uapi/asm obj=arch/arm64/include/generated/uapi/asm set -e; : ' CHK include/generated/uapi/linux/version.h'; mkdir -p include/generated/uapi/linux/; (echo \#define LINUX_VERSION_CODE 263239; echo '#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))';) < Makefile > include/generated/uapi/linux/version.h.tmp; if [ -r include/generated/uapi/linux/version.h ] && cmp -s include/generated/uapi/linux/version.h include/generated/uapi/linux/version.h.tmp; then rm -f include/generated/uapi/linux/version.h.tmp; else : ' UPD include/generated/uapi/linux/version.h'; mv -f include/generated/uapi/linux/version.h.tmp include/generated/uapi/linux/version.h; fi rm -f include/linux/version.h set -e; : ' CHK include/generated/utsrelease.h'; mkdir -p include/generated/; if [ `echo -n "4.4.71-00005-g2fc62ed91813-dirty" | wc -c ` -gt 64 ]; then echo '"4.4.71-00005-g2fc62ed91813-dirty" exceeds 64 characters' >&2; exit 1; fi; (echo \#define UTS_RELEASE \"4.4.71-00005-g2fc62ed91813-dirty\";) < include/config/kernel.release > include/generated/utsrelease.h.tmp; if [ -r include/generated/utsrelease.h ] && cmp -s include/generated/utsrelease.h include/generated/utsrelease.h.tmp; then rm -f include/generated/utsrelease.h.tmp; else : ' UPD include/generated/utsrelease.h'; mv -f include/generated/utsrelease.h.tmp include/generated/utsrelease.h; fi mkdir -p .tmp_versions ; rm -f .tmp_versions/* make -f ./scripts/Makefile.build obj=scripts/basic (cat /dev/null; ) > scripts/basic/modules.order rm -f .tmp_quiet_recordmcount make -f ./scripts/Makefile.build obj=. (cat /dev/null; ) > modules.order mkdir -p kernel/
- 仅Ubuntu 24.04会强制重新编译
kernel/bounds.s:
aarch64-linux-gnu-gcc -Wp,-MD,kernel/.bounds.s.d -nostdinc -isystem /home/gregoire/tmp-inout/gcc-linaro-5.3.1-2016.05-x86_64_aarch64-linux-gnu/bin/../lib/gcc/aarch64-linux-gnu/5.3.1/include -I./arch/arm64/include -Iarch/arm64/include/generated/uapi -Iarch/arm64/include/generated -Iinclude -I./arch/arm64/include/uapi -Iarch/arm64/include/generated/uapi -I./include/uapi -Iinclude/generated/uapi -include ./include/linux/kconfig.h -D__KERNEL__ -mlittle-endian -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -Werror-implicit-function-declaration -Wno-format-security -std=gnu89 -fno-PIE -mgeneral-regs-only -DCONFIG_AS_LSE=1 -mpc-relative-literal-loads -fno-delete-null-pointer-checks -Wno-maybe-uninitialized -Os --param=allow-store-data-races=0 -Wframe-larger-than=1024 -fno-stack-protector -Wno-unused-but-set-variable -fno-omit-frame-pointer -fno-optimize-sibling-calls -fno-var-tracking-assignments -g -pg -Wdeclaration-after-statement -Wno-pointer-sign -fno-strict-overflow -fconserve-stack -Werror=implicit-int -Werror=strict-prototypes -Werror=date-time -DCC_HAVE_ASM_GOTO -D"KBUILD_STR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(bounds)" -D"KBUILD_MODNAME=KBUILD_STR(bounds)" -fverbose-asm -S -o kernel/bounds.s kernel/bounds.c
相关文件与文件系统信息
Ubuntu 24.04下kernel/bounds.c和kernel/bounds.s的属性:
Ubuntu 24.04: File: kernel/bounds.c Size: 703 Blocks: 8 IO Block: 4096 regular file Device: 259,2 Inode: 14559530 Links: 1 Access: (0664/-rw-rw-r--) Uid: ( 1000/gregoire) Gid: ( 1000/gregoire) Access: 2024-05-14 10:05:28.629197631 -0700 Modify: 2017-09-23 09:19:33.558726253 -0700 Change: 2024-05-07 15:44:11.170012538 -0700 Birth: 2024-04-26 08:54:22.911042385 -0700 File: kernel/bounds.s Size: 14617 Blocks: 32 IO Block: 4096 regular file Device: 259,2 Inode: 14559336 Links: 1 Access: (0664/-rw-rw-r--) Uid: ( 1000/gregoire) Gid: ( 1000/gregoire) Access: 2024-05-14 10:26:03.478134345 -0700 Modify: 2024-05-14 10:26:03.467134218 -0700 Change: 2024-05-14 10:26:03.467134218 -0700 Birth: 2024-05-04 21:27:59.503527455 -0700
文件系统配置:
Ubuntu 24.04: /dev/nvme0n1p2 on / type ext4 (rw,relatime,stripe=32) Ubuntu 20.04: /dev/sda5 on / type ext4 (rw,relatime,errors=remount-ro)
原因分析
1. bounds.s重编译触发全局依赖连锁反应
kernel/bounds.s是生成include/generated/bounds.h的直接依赖,而bounds.h是内核全局头文件,几乎所有内核代码都会包含它。Ubuntu 24.04中每次编译都会重新生成bounds.s,哪怕内容无变化,仅修改时间戳,都会导致Make认为bounds.h需要更新,进而触发所有依赖bounds.h的目标文件重编译,最终引发全量编译。
2. Make工具与文件系统的时间戳逻辑差异
- Make版本差异:Ubuntu 24.04默认使用GNU Make 4.3.90,而Ubuntu 20.04使用Make 4.2.1。新版本Make对文件时间戳的对比逻辑更严格,尤其是支持纳秒级精度的时间戳校验,而旧版本生成的
bounds.s时间戳精度较低,导致新版本Make误判bounds.c的修改时间晚于bounds.s,触发重编译。 - 文件系统配置差异:Ubuntu 24.04的ext4启用了
stripe=32参数,可能影响文件时间戳的更新行为;同时NVMe盘的ext4默认支持更高精度的时间戳,与Ubuntu 20.04使用的SATA盘ext4时间精度不匹配,加剧了时间戳对比的误判。
3. Shell版本导致的临时文件处理差异
Ubuntu 24.04使用bash 5.2.21,而Ubuntu 20.04使用bash 5.0.17。不同版本Shell在处理临时文件生成、移动时,可能导致文件的修改时间(mtime)被意外更新,间接触发后续依赖链的重编译逻辑。
解决方案建议
- 强制使用秒级时间戳对比:编译前执行
export POSIXLY_CORRECT=1,让Make忽略纳秒级时间戳差异,避免误判文件更新。 - 修改内核Makefile规则:针对
kernel/bounds.s的生成规则,添加内容校验逻辑,仅当文件内容实际变化时才更新文件,而非仅依赖时间戳。 - 统一文件系统挂载参数:将Ubuntu 24.04的ext4挂载参数调整为与20.04一致(移除
stripe=32),测试是否解决时间戳相关问题。 - 降级Make版本:在Ubuntu 24.04中安装Make 4.2.1,匹配Ubuntu 20.04的环境配置。
内容的提问来源于stack exchange,提问作者gregoiregentil
相关产品推荐
相关产品推荐

