title: "Linux内核编译与配置" tags:
Linux内核版本号由三个数字组成:主版本.次版本.修订号。例如Linux 4.1.15,其中:
4:主版本号,重大架构变更时递增1:次版本号,奇数表示开发版,偶数表示稳定版15:修订号,bug修复和小功能更新查看内核版本:
uname -r
# 输出示例:4.1.15-gb39c1d0
内核源码获取(教材 35.1):Linux 由 Linux 基金会管理与发布,官网为 https://www.kernel.org,最新稳定版可直接在官网下载。NXP 从官网下载某个版本的内核,移植到自己的 CPU 上并测试通过后,再开放给 NXP CPU 的开发者。本章移植使用 NXP 提供的源码,路径为:
开发板光盘->1、例程源码->4、NXP 官方原版 Uboot 和 Linux->
linux-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2
正点原子出厂源码路径为:
开发板光盘->1、例程源码->3、正点原子 Uboot 和 Linux 出厂源码->
linux-imx-4.1.15-2.1.0-g8a006db.tar.bz2
注意:正点原子出厂系统在持续更新,压缩包名字可能不同,以实际为准。
编译后的 Linux 源码目录(教材表 35.3.1)主要文件夹与文件如下:
linux-4.1.15/
├── arch/ # 架构相关目录(arm、arm64、x86 等)
├── block/ # 块设备相关目录(SD卡、EMMC、NAND、硬盘)
├── crypto/ # 加密相关目录(crc、crc32、md4、md5、hash)
├── Documentation/ # 文档相关目录
├── drivers/ # 驱动相关目录(学习重点)
├── firmware/ # 固件相关目录
├── fs/ # 文件系统相关目录(ext2/ext4/f2fs 等)
├── include/ # 头文件相关目录
├── init/ # 初始化相关目录(内核启动初始化代码)
├── ipc/ # 进程间通信相关目录
├── kernel/ # 内核核心相关目录
├── lib/ # 库函数相关目录
├── mm/ # 内存管理相关目录
├── net/ # 网络相关目录
├── samples/ # 例程相关目录
├── scripts/ # 脚本相关目录(编译时大量使用)
├── security/ # 安全相关目录
├── sound/ # 音频处理相关目录
├── tools/ # 工具相关目录
├── usr/ # 与 initramfs 相关的目录
├── virt/ # 提供虚拟机技术(KVM)
├── .config # 最终使用的配置文件(编译生成)
├── Kbuild # Makefile 会读取此文件
├── Kconfig # 图形化配置界面的配置文件
├── Makefile # Linux 顶层 Makefile
├── README # 编译说明与源码目录说明(建议仔细阅读)
├── System.map # 符号表(编译生成)
├── vmlinux # 未压缩的 ELF 格式 Linux 文件(编译生成)
└── vmlinux.o # 编译生成的 vmlinux.o
以 arch/arm 为例,其下还有 boot、common、configs、mach-xxx 等子目录,分别用于控制系统引导、系统调用、动态调频、主频设置等:
arch/arm/configs/ — 各平台的默认配置文件 xxx_defconfig,其中 imx_v7_defconfig 即 I.MX6U-ALPHA 开发板的默认配置;arch/arm/boot/dts/ — 对应开发平台的设备树文件;arch/arm/boot/ — 保存编译出来的 Image 和 zImage 镜像,zImage 就是最终要用的 Linux 镜像;arch/arm/mach-xxx/ — 相应平台的驱动和初始化文件,如 mach-imx 就是 I.MX 系列 CPU 的驱动与初始化文件。Linux内核使用Kconfig配置系统,主要文件包括:
Kconfig:配置项定义Makefile:编译规则defconfig:默认配置文件配置流程:Kconfig → menuconfig → .config → 编译
⚠️ 来源说明:本节不属于《I.MX6U嵌入式Linux驱动开发指南》内容,为扩展知识。
Kconfig是Linux内核的配置描述语言,定义在各子目录的Kconfig文件中。
config 选项名称
类型(bool/tristate/int/hex/string)
"提示信息"
默认值
依赖关系
帮助文本
| 类型 | 说明 | 取值 |
|---|---|---|
bool |
布尔类型 | y或n |
tristate |
三态类型 | y、n或m(模块) |
int |
整数类型 | 数字 |
hex |
十六进制 | 0x格式数字 |
string |
字符串 | 任意字符串 |
# 定义配置项
config LED_DRIVER
bool "LED Driver"
default y
# 三态类型(可编译为模块)
config USB_STORAGE
tristate "USB storage support"
default m
# 整数类型
config NR_CPUS
int "Maximum number of CPUs"
range 2 8
default 4
# 依赖关系
config HIGHMEM
bool "High Memory Support"
depends on ARM
# 反向依赖
config FB
bool "Support for frame buffer"
depends on !S390
# 选择依赖
config SND_SOC
tristate "ALSA SoC support"
select SND_PCM
# 多选依赖
config EXT4_FS
tristate "Ext4 file system"
depends on BLOCK
# 创建菜单
menu "Device Drivers"
source "drivers/Kconfig"
endmenu
# 创建子菜单
menu "Network device support"
menu "Ethernet driver support"
source "drivers/net/ethernet/Kconfig"
endmenu
endmenu
# 条件菜单
if ARCH_ARM
menu "ARM specific configuration"
endmenu
endif
依赖关系是Kconfig的核心机制,确保配置选项的逻辑正确性。
# depends on - 必须满足
config MODULE_UNLOAD
bool "Module unloading"
depends on MODULES
# ! 取反
config LOCALVERSION
string "Local version - append to kernel release"
depends on !COMPILE_TEST
# || 或
config SND
tristate "Sound card support"
depends on AUDIT || !AUDIT
# && 与
config X86_64
bool "64-bit kernel"
depends on 64BIT && X86
# 选择依赖(自动选择被依赖项)
config SND_SOC
tristate "ALSA SoC support"
select SND_PCM
# 反向选择
config SND_SOC_I2S
tristate "Generic I2S driver"
select SND_SOC
make menuconfig是基于ncurses的图形配置界面。
# 基本使用
make menuconfig
# 交叉编译配置
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
# 使用外部分支配置
make ARCH=arm menuconfig O=out/
配置界面操作:
y:编译进内核m:编译为模块n:不编译?:查看帮助/:搜索配置项Esc Esc:返回上级顶层Makefile一开始(第 1~4 行)就定义了 Linux 内核的版本号:
VERSION = 4
PATCHLEVEL = 1
SUBLEVEL = 15
EXTRAVERSION =
可以看出,Linux 内核版本号为 4.1.15。教材 35.5 只列出了上面 4 行;KERNELVERSION 由这 4 个变量拼接而成,其完整定义(摘自同一份顶层 Makefile)为:
KERNELVERSION = $(VERSION)$(if $(PATCHLEVEL),.$(PATCHLEVEL)$(if $(SUBLEVEL),.$(SUBLEVEL)))$(EXTRAVERSION)
# 递归编译标志
MAKEFLAGS += -rR --include-dir=$(CURDIR)
-r禁止内置规则,-R禁止内置变量,提高编译效率。
# V=1 显示详细编译命令
ifeq ("$(origin V)", "command line")
KBUILD_VERBOSE = $(V)
endif
ifndef KBUILD_VERBOSE
KBUILD_VERBOSE = 0
endif
ifeq ($(KBUILD_VERBOSE),1)
quiet =
Q =
else
quiet=quiet_
Q = @
endif
export quiet Q KBUILD_VERBOSE
使用方法:
make V=0:静默输出(默认)make V=1:显示完整编译命令# make -s 静默模式
ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4
ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),)
quiet=silent_
endif
else # make-3.8x
ifneq ($(filter s% -s%,$(MAKEFLAGS)),)
quiet=silent_
endif
endif
# O=xxx 指定输出目录
ifeq ($(KBUILD_SRC),)
ifeq ("$(origin O)", "command line")
KBUILD_OUTPUT := $(O)
endif
endif
使用方法:
make O=out/linux # 编译输出到out/linux目录
# C=1 检查需要重新编译的文件
# C=2 检查所有源文件
ifeq ("$(origin C)", "command line")
KBUILD_CHECKSRC = $(C)
endif
ifndef KBUILD_CHECKSRC
KBUILD_CHECKSRC = 0
endif
# M=dir 编译外部模块
ifdef SUBDIRS
KBUILD_EXTMOD ?= $(SUBDIRS)
endif
ifeq ("$(origin M)", "command line")
KBUILD_EXTMOD := $(M)
endif
# 外部模块时,目标为modules
PHONY += all
ifeq ($(KBUILD_EXTMOD),)
_all: all
else
_all: modules
endif
使用方法:
make M=/path/to/module # 编译外部模块
ifeq ($(KBUILD_SRC),)
srctree := .
else
ifeq ($(KBUILD_SRC)/,$(dir $(CURDIR)))
srctree := ..
else
srctree := $(KBUILD_SRC)
endif
endif
objtree := .
src := $(srctree)
obj := $(objtree)
VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))
export srctree objtree VPATH
# 架构类型
ARCH ?= $(SUBARCH)
CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%)
实际使用时需要修改为:
ARCH ?= arm
CROSS_COMPILE ?= arm-linux-gnueabihf-
或者通过命令行指定:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- xxx_defconfig
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
USERINCLUDE := \
-I$(srctree)/arch/$(hdr-arch)/include/uapi \
-Iarch/$(hdr-arch)/include/generated/uapi \
-I$(srctree)/include/uapi \
-Iinclude/generated/uapi \
-include $(srctree)/include/linux/kconfig.h
LINUXINCLUDE := \
-I$(srctree)/arch/$(hdr-arch)/include \
-Iarch/$(hdr-arch)/include/generated/uapi \
-Iarch/$(hdr-arch)/include/generated \
$(if $(KBUILD_SRC), -I$(srctree)/include) \
-Iinclude \
$(USERINCLUDE)
export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION
export ARCH SRCARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC
export CPP AR NM STRIP OBJCOPY OBJDUMP
export MAKE AWK GENKSYMS INSTALLKERNEL PERL PYTHON UTS_MACHINE
export HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS
export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS
export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE CFLAGS_GCOV CFLAGS_KASAN
export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE
export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_LDFLAGS_MODULE
export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL
export KBUILD_ARFLAGS
顶层Makefile会包含文件 scripts/Kbuild.include(教材 35.5 第 9 点):
# We need some generic definitions (do not try to remake the file).
scripts/Kbuild.include: ;
include scripts/Kbuild.include
其中定义了后面反复使用的 build、if_changed 等函数,例如:
build := -f $(srctree)/scripts/Makefile.build obj
make xxx_defconfig 配置内核时,实际执行的是下面两条命令,它们都会用到 scripts/Makefile.build(教材 35.5.2):
@make -f ./scripts/Makefile.build obj=scripts/basic
@make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig
(1)scripts_basic 目标
scripts_basic:
$(Q)$(MAKE) $(build)=scripts/basic
$(Q)rm -f .tmp_quiet_recordmcount
展开后即 @make -f ./scripts/Makefile.build obj=scripts/basic。Makefile.build 读取 scripts/basic/Makefile,其默认目标 __build 中只有 always 有效:
__build: scripts/basic/fixdep scripts/basic/bin2c
@:
所以 scripts_basic 的作用就是编译出 scripts/basic/fixdep 和 scripts/basic/bin2c 两个主机工具。
(2)%config 目标
顶层Makefile中有 config 和 %config 两个目标:
config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@
%config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@
make xxx_defconfig 与 %config 匹配。Makefile.build 会读取 scripts/kconfig/Makefile,其中的规则为:
%_defconfig: $(obj)/conf
$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)
展开后为:
@scripts/kconfig/conf --defconfig=arch/arm/configs/%_defconfig Kconfig
%_defconfig 依赖 scripts/kconfig/conf,因此会先编译 scripts/kconfig/conf.c 生成 conf 工具,再由它将默认配置输出为内核根目录下的 .config 文件。
首次编译前需要使用make xxx_defconfig配置内核。教材提供的 I.MX6U-ALPHA 默认配置文件为 arch/arm/configs/imx_v7_defconfig:
# 使用默认配置
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v7_defconfig
顶层Makefile中与之匹配的目标(教材示例代码 35.5.1.1):
config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@
%config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@
此时 config-targets=1、mixed-targets=0、dot-config=1。%config 第 534 行会 include arch/$(SRCARCH)/Makefile(SRCARCH=arm),这个文件很重要,zImage、uImage 等镜像就是由它生成的。
执行流程:
make xxx_defconfig
↓
scripts_basic (编译fixdep、bin2c工具)
↓
scripts/kconfig/conf --defconfig=arch/arm/configs/xxx_defconfig Kconfig
↓
生成.config文件
完整的一次编译(教材 35.2)通常先清理再配置,最后编译:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v7_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j16
注意:
distclean会连带删除.config等配置信息。若已通过图形界面配置了内核但还没保存新的配置文件,则要慎用该脚本,否则配置会被全部删除。
图形化配置界面:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
配置文件位置:
arch/arm/configs/xxx_defconfig.config# 使用架构默认配置
make ARCH=arm defconfig
# 使用厂商提供的配置
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v7_defconfig
# 方式1:基于现有配置修改
make menuconfig
# 在菜单中修改配置,保存后生成.config
# 方式2:手动编辑.config
vim .config
make olddefconfig # 更新配置
# 方式3:备份和恢复配置
cp .config .config.backup
make defconfig
cp .config.backup .config
make olddefconfig
安装 lzop(教材 35.2):编译内核前需要先在 Ubuntu 上安装 lzop 库,否则编译会失败:
sudo apt-get install lzop
否则会提示 recipe for target 'arch/arm/boot/compressed/piggy.lzo' failed(lzop 未找到),安装后重新编译即可。
正点原子编译脚本 mx6ull_alientek_emmc.sh:
#!/bin/sh
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v7_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j16
make distclean:清理工程,每次运行脚本都会先清理;make xxx_defconfig:配置工程;make menuconfig:打开图形配置界面,不需要每次配置时可删除此行;make:编译 Linux 源码。图形配置界面和 U-Boot 一样,本实验不需要任何配置,直接按两下 ESC 键退出,退出后会自动开始编译。
编译完成后:
arch/arm/boot/zImage — 要用的 Linux 镜像文件;arch/arm/boot/dts/*.dtb — 很多设备树文件。# 编译zImage
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage
# 编译所有(包括模块)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all
# 使用16线程并行编译
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16
# make all 目标
all: vmlinux
# BOOT_TARGETS定义(arch/arm/Makefile)
BOOT_TARGETS = zImage Image xipImage bootpImage uImage
$(BOOT_TARGETS): vmlinux
$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
BOOT_TARGETS 依赖 vmlinux,所以编译 zImage 时会先编译出 vmlinux。以 zImage 为例,第 316 行命令展开后为:
@make -f ./scripts/Makefile.build obj=arch/arm/boot MACHINE=arch/arm/boot/zImage
即再次借助 scripts/Makefile.build 完成从 vmlinux 到 zImage 的转换。使用 make、make all、make zImage 均可编译出 zImage。
# 第 905~907 行:导出给 scripts/link-vmlinux.sh 使用
export KBUILD_VMLINUX_INIT := $(head-y) $(init-y)
export KBUILD_VMLINUX_MAIN := $(core-y) $(libs-y) $(drivers-y) $(net-y)
export KBUILD_LDS := arch/$(SRCARCH)/kernel/vmlinux.lds
# 第 912 行:vmlinux依赖
vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN)
# 第 915 行:链接命令
cmd_link-vmlinux = $(CONFIG_SHELL) $< $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux)
# 第 920 行:vmlinux目标
vmlinux: scripts/link-vmlinux.sh $(vmlinux-deps) FORCE
+$(call if_changed,link-vmlinux)
其中:
KBUILD_LDS = arch/arm/kernel/vmlinux.lds,是整个 Linux 的链接脚本;$(head-y) 定义在 arch/arm/Makefile:head-y := arch/arm/kernel/head$(MMUEXT).o,使能 MMU 时 MMUEXT 为空,故 head-y = arch/arm/kernel/head.o;init-y/drivers-y/net-y 由顶层Makefile用 patsubst 把目录名替换为 %/built-in.o;libs-y 的值为 arch/arm/lib/lib.a lib/lib.a arch/arm/lib/built-in.o lib/built-in.o;core-y 在 arch/arm/Makefile 中被追加了 arch/arm/kernel、mm、common、probes、net、crypto、firmware、mach-imx 等目录。最终链接的实际命令(LDFLAGS_vmlinux=-p --no-undefined -X --pic-veneer --build-id):
/bin/bash scripts/link-vmlinux.sh arm-linux-gnueabihf-ld -EL -p --no-undefined -X --pic-veneer --build-id
scripts/link-vmlinux.sh 中的 vmlinux_link() 以 ${lds} 为链接脚本、以 ${KBUILD_VMLINUX_INIT} 和 ${KBUILD_VMLINUX_MAIN} 为输入文件完成最终链接。
vmlinux组成部分:
| 组成 | 说明 | 路径 |
|---|---|---|
| head-y | 启动代码 | arch/arm/kernel/head.o |
| init-y | 初始化代码 | init/built-in.o |
| core-y | 核心代码 | kernel/mm/fs/ipc等 |
| libs-y | 库函数 | lib/lib.a lib/built-in.o |
| drivers-y | 设备驱动 | drivers/sound/firmware |
| net-y | 网络协议 | net/built-in.o |
| 镜像 | 大小 | 说明 |
|---|---|---|
| vmlinux | ~16MB | ELF格式,未压缩 |
| Image | ~12MB | 去除调试信息 |
| zImage | ~6MB | gzip压缩 |
| uImage | ~6MB | U-Boot专用格式 |
转换关系:
vmlinux → objcopy → Image → gzip → zImage → mkimage → uImage
教材 35.5.5 的详细说明:
objcopy 取消掉 vmlinux 中的符号表等信息;没有压缩,保存在 arch/arm/boot 目录下,约 12MB;# 编译后修改代码,直接执行(只重编被修改的文件)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16
# 清理编译产物(保留 .config)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- clean
# 完全清理(删除所有生成文件 + .config 及备份文件)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mrproper
# 在 mrproper 基础上再删除编辑器备份与补丁文件(教材编译脚本使用)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
教材 35.5.4 指出,vmlinux-deps 中除链接脚本外,其余都是各子目录下的 built-in.o、.a 文件。顶层Makefile中:
# 第 937 行:vmlinux-deps 依赖 vmlinux-dirs
$(sort $(vmlinux-deps)): $(vmlinux-dirs) ;
# 第 889 行:vmlinux-dirs 保存生成 vmlinux 所需的源码目录
vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \
$(core-y) $(core-m) $(drivers-y) $(drivers-m) \
$(net-y) $(net-m) $(libs-y) $(libs-m)))
# 第 946~947 行:进入每个目录执行编译
$(vmlinux-dirs): prepare scripts
$(Q)$(MAKE) $(build)=$@
$(vmlinux-dirs) 规则的命令展开后就是为 init、usr、arch/arm/kernel、kernel、mm、fs、drivers、lib 等每个目录各执行一次:
@make -f ./scripts/Makefile.build obj=<目录名>
每个目录下生成的 built-in.o 由 scripts/Makefile.build 中的 builtin-target 定义:
ifneq ($(strip $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)),)
builtin-target := $(obj)/built-in.o
endif
$(builtin-target): $(obj-y) FORCE
$(call if_changed,link_o_target)
cmd_link_o_target 使用 $(LD) ... -r -o $@ 把该目录下所有 obj-y 指定的 .o 文件链接成一个 built-in.o。最终所有 built-in.o 与 .a 文件被链接成 ELF 可执行文件 vmlinux。
使用 O=xxx 可把编译产生的过程文件输出到指定目录,源码目录保持干净:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- O=out/linux imx_v7_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- O=out/linux -j16
在Kconfig中使用obj-y编译进内核:
# drivers/leds/Kconfig
config LEDS_GPIO
tristate "GPIO LED driver"
depends on LEDS_CLASS
default y
# drivers/leds/Makefile
obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o
# 编译外部模块
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- M=/path/to/module modules
# 安装模块到指定目录
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- M=/path/to/module \
INSTALL_MOD_PATH=/path/to/rootfs modules_install
⚠️ 来源说明:本节不属于《I.MX6U嵌入式Linux驱动开发指南》第三十五章内容,为扩展知识。教材仅在 35.5 第 7 点讲解
make M=dir的模块编译机制,完整的模块 Makefile 模板见第四十章。
# 外部模块Makefile
obj-m := mydriver.o
mydriver-objs := main.o init.o
KERNEL_DIR := /path/to/linux-4.1.15
CROSS_COMPILE := arm-linux-gnueabihf-
ARCH := arm
all:
make -C $(KERNEL_DIR) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) \
M=$(PWD) modules
clean:
make -C $(KERNEL_DIR) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) \
M=$(PWD) clean
# 将模块复制到内核模块目录
cp mydriver.ko /lib/modules/4.1.15/kernel/drivers/
# 生成模块依赖
depmod -a
# 加载模块
insmod mydriver.ko
# 查看已加载模块
lsmod
⚠️ 来源说明:本节不属于《I.MX6U嵌入式Linux驱动开发指南》内容,为扩展知识。
| 对比项 | I.MX6ULL | STM32MP1 | RK3568 |
|---|---|---|---|
| 内核版本 | 4.1.15 | 5.4 | 5.10 |
| 架构 | ARM Cortex-A7 | ARM Cortex-A7 | ARM Cortex-A55 |
| 位数 | 32位 | 32位 | 64位 |
| 配置文件 | imx_v7_defconfig | stm32mp1_defconfig | rockchip_linux_defconfig |
| 设备树 | imx6ull-14x14-evk.dts | stm32mp157c-ev1.dts | rk3568-evb1-v10.dts |
| 内核大小 | ~5MB | ~6MB | ~8MB |
| 启动方式 | U-Boot+zImage | U-Boot+zImage | U-Boot+zImage |
# I.MX6ULL (ARM 32位)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v7_defconfig
# STM32MP1 (ARM 32位)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- stm32mp1_defconfig
# RK3568 (ARM 64位)
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- rockchip_linux_defconfig
| 驱动类型 | I.MX6ULL | STM32MP1 | RK3568 |
|---|---|---|---|
| GPIO | gpio-mxc | gpio-stm32 | gpio-rockchip |
| I2C | i2c-imx | i2c-stm32 | i2c-rk3x |
| SPI | spi-imx | spi-stm32 | spi-rockchip |
| UART | serial-imx | serial-stm32 | serial-rockchip |
⚠️ 来源说明:本节不属于《I.MX6U嵌入式Linux驱动开发指南》内容,为扩展知识。
答案:
make xxx_defconfig或make menuconfig生成.config文件arch/arm/Makefile设置架构相关变量scripts/link-vmlinux.sh链接生成vmlinux答案:
depends on:配置项被依赖,必须满足条件才能启用
config HIGHMEM
bool "High Memory Support"
depends on ARM # 只有ARM架构才能选择
select:配置项被选中时自动选择被依赖项
config SND_SOC
tristate "ALSA SoC support"
select SND_PCM # 选中时自动选中SND_PCM
答案: vmlinux通过链接脚本和链接命令生成:
答案:
make xxx_defconfig:使用预定义的默认配置文件快速配置,适合初始配置
make ARCH=arm imx_v7_defconfig # 使用IMX6ULL默认配置
make menuconfig:提供图形界面手动修改配置,适合定制化需求
make ARCH=arm menuconfig # 打开配置菜单
推荐流程:先用defconfig快速配置,再用menuconfig精细调整
答案:
# 1. 进入内核源码目录
cd /path/to/linux-4.1.15
# 2. 配置内核
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v7_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
# 3. 编译内核
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16
# 4. 编译模块
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules
# 5. 安装模块到rootfs
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- \
INSTALL_MOD_PATH=/path/to/rootfs modules_install
# 6. 复制zImage到TFTP目录
cp arch/arm/boot/zImage /tftpboot/
# 7. 复制设备树到TFTP目录
cp arch/arm/boot/dts/imx6ull-alientek-emmc.dtb /tftpboot/
# 8. 在U-Boot中设置启动参数
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
setenv bootcmd 'tftp 80800000 zImage; tftp 83000000 imx6ull-alientek-emmc.dtb; bootz 80800000 - 83000000'
saveenv
run bootcmd
内容来源: 《I.MX6U嵌入式Linux驱动开发指南》第三十五章 Linux内核顶层Makefile详解