网站首页 > 开源技术 正文
**Linux 内核源码树根目录下的 `Makefile`**。这是一个非常核心且复杂的文件,它的主要作用是**组织和驱动整个 Linux 内核的编译、配置和安装过程**。可以把它看作是构建 Linux 内核的总指挥或“菜谱”。
由于其复杂性,我会分块解释其主要功能和含义,并尽量详细:
**一、 文件头部与基本信息**
```makefile
# SPDX-License-Identifier: GPL-2.0
VERSION = 6
PATCHLEVEL = 15
SUBLEVEL = 0
EXTRAVERSION = -rc1
NAME = Baby Opossum Posse
```
* **`SPDX-License-Identifier`**: 指定了该文件的许可证是 GPL-2.0。
* **`VERSION`, `PATCHLEVEL`, `SUBLEVEL`, `EXTRAVERSION`, `NAME`**: 定义了当前内核的版本号和名称。这些变量组合起来构成内核的版本字符串(例如 `6.15.0-rc1`),并用于各种地方,如模块路径、版本信息显示等。`NAME` 是一个有趣的代号。
**二、 文档与帮助信息**
```makefile
# *DOCUMENTATION*
# To see a list of typical targets execute "make help"
# More info can be located in ./README
# Comments in this file are targeted only to the developer, do not
# expect to learn how to build the kernel reading this file.
```
* 明确指出可以通过 `make help` 获取常用目标列表。
* 说明更多信息在 `README` 文件中。
* 强调此文件的注释主要面向开发者,而非初学者教程。
**三、 GNU Make 版本检查与内部目标限制**
```makefile
ifeq ($(filter output-sync,$(.FEATURES)),)
$(error GNU Make >= 4.0 is required. Your Make version is $(MAKE_VERSION))
endif
$(if $(filter __%, $(MAKECMDGOALS)), \
$(error targets prefixed with '__' are only for internal use))
```
* 检查 `make` 命令的版本是否 `>= 4.0`,因为内核构建系统依赖特定版本的 Make 功能。如果版本过低,会报错并退出。
* 禁止用户直接调用以 `__` 开头的内部目标,这些目标仅供 Makefile 内部逻辑使用。
**四、 默认目标与递归构建说明**
```makefile
# That's our default target when none is given on the command line
PHONY := __all
__all:
# ... (递归构建的说明注释) ...
```
* `__all` 是默认目标。如果在命令行没有指定目标(直接运行 `make`),则会执行 `__all` 目标。
* 注释解释了 Linux 内核使用**递归构建 (Recursive Build)** 的方式。这意味着顶层 Makefile 会调用子目录中的 Makefile 来构建各个部分。这种方式的关键在于确保正确的构建顺序和依赖关系,子 Makefile 通常只修改自己目录下的文件。
**五、 路径设置与环境准备 (首次调用时)**
```makefile
this-makefile := $(lastword $(MAKEFILE_LIST))
abs_srctree := $(realpath $(dir $(this-makefile))) # 内核源代码根目录的绝对路径
abs_output := $(CURDIR) # 当前工作目录,默认是输出目录
ifneq ($(sub_make_done),1) # 检查是否是首次调用
# ... (设置 MAKEFLAGS, LC_*, GREP_OPTIONS 等环境变量) ...
# ... (设置编译输出的详细程度 KBUILD_VERBOSE, quiet, Q) ...
# ... (设置静态代码检查 KBUILD_CHECKSRC, C=1/2) ...
# ... (设置 Rust linter KBUILD_CLIPPY, CLIPPY=1) ...
# ... (处理外部模块构建 KBUILD_EXTMOD, M=dir) ...
# ... (处理额外的警告 W=n) ...
# ... (处理输出目录 KBUILD_OUTPUT, O=dir) ...
# 确定 objtree (实际编译输出根目录) 和 srcroot (源码根目录,对于外部模块可能是模块目录)
export sub_make_done := 1 # 标记首次调用已完成
endif # sub_make_done
```
* 这段代码只在 `make` 命令第一次被调用时执行(通过 `sub_make_done` 变量控制)。
* **路径确定:** 获取源代码树 (`abs_srctree`) 和当前工作目录 (`abs_output`) 的绝对路径。
* **环境变量清理:** 清理一些可能干扰构建的环境变量,如 `LC_ALL`, `GREP_OPTIONS`,并设置 `LC_COLLATE`, `LC_NUMERIC` 为 `C` 以保证一致性。
* **输出控制 (`V=`, `quiet`, `Q`):** 设置构建过程的输出详细程度。`make V=1` 会显示完整的编译命令。`@` 符号 (`Q`) 用于在非 verbose 模式下隐藏命令本身。
* **静态检查 (`C=1/2`, `KBUILD_CHECKSRC`):** 配置是否在编译 C 代码时运行静态检查工具(如 `sparse`)。`C=1` 检查重新编译的文件,`C=2` 检查所有文件。
* **Rust Linter (`CLIPPY=1`, `KBUILD_CLIPPY`):** 配置是否在编译 Rust 代码时运行 `clippy` linter。
* **外部模块 (`M=dir`, `KBUILD_EXTMOD`):** 处理构建内核树之外的模块(驱动程序等)。`M=dir` 指定外部模块的源代码目录。
* **额外警告 (`W=n`, `KBUILD_EXTRA_WARN`):** 允许用户启用额外的编译器警告级别。
* **输出目录 (`O=dir`, `KBUILD_OUTPUT`):** 这是非常重要的功能,允许用户将编译生成的文件(包括 `.config`、目标文件、最终内核镜像等)放在与源代码树不同的目录中,保持源代码树的清洁。`O=` 命令行参数优先于 `KBUILD_OUTPUT` 环境变量。
* `objtree`: 最终确定的编译输出根目录。
* `srcroot`: 对于内核构建是源码根目录,对于外部模块构建是该模块的源码目录。
* **标记完成:** 设置 `sub_make_done := 1`,这样下次递归调用 `make` 时就不会再执行这段初始化代码。
**六、 递归调用处理**
```makefile
ifeq ($(abs_output),$(CURDIR))
# ... (如果在最终输出目录,设置 --no-print-directory) ...
else
# ... (需要递归调用) ...
need-sub-make := 1
endif
# ... (处理 Make 4.4.1 的兼容性问题) ...
ifeq ($(need-sub-make),1)
# ... (定义 __sub-make 目标) ...
__sub-make:
$(Q)$(MAKE) $(no-print-directory) -C $(abs_output) \
-f $(abs_srctree)/Makefile $(MAKECMDGOALS)
else
# ... (进入实际的构建逻辑) ...
endif
```
* 这段逻辑判断当前 `make` 调用是否在最终的输出目录 (`abs_output`) 中。
* 如果**不在**(通常是用户直接在源代码目录运行 `make O=...` 的情况),则设置 `need-sub-make := 1`。
* 如果 `need-sub-make` 为 1,则定义一个 `__sub-make` 目标。其他所有目标(或默认的 `__all`)都依赖于 `__sub-make`。
* `__sub-make` 规则的作用是:**再次调用 `make` 命令,但这次使用 `-C $(abs_output)` 切换到指定的输出目录**,并使用 `-f $(abs_srctree)/Makefile` 指定使用源代码树中的 Makefile。这样后续所有的构建操作都在输出目录中进行。
* `$(no-print-directory)` 用于控制是否显示 `make` 进入/离开目录的消息。
**七、 实际构建逻辑 (在最终输出目录中执行时)**
这部分是当 `make` 最终在 `abs_output` 目录(可能是 `.`,也可能是 `O=` 指定的目录)中执行时的核心逻辑。
```makefile
# ... (确定 srcroot, srctree, VPATH 等变量) ...
# ... (处理 *config 目标,调用 scripts/kconfig/Makefile) ...
# 区分纯配置目标和混合目标 (如 make oldconfig all)
# 判断是否需要读取 .config (need-config)
# 判断是否需要同步配置 (may-sync-config)
include $(srctree)/scripts/Kbuild.include # 包含 Kbuild 辅助函数
# ... (读取 KERNELRELEASE, 设置 KERNELVERSION) ...
# ... (设置架构相关的变量 ARCH, SRCARCH, UTS_MACHINE) ...
# ... (设置交叉编译 CROSS_COMPILE) ...
# ... (设置 KCONFIG_CONFIG 文件路径) ...
# ... (设置 HOST 工具链变量 HOSTCC, HOSTCXX, HOSTRUSTC 等) ...
# ... (设置 HOST 编译/链接标志 KBUILD_HOSTCFLAGS, KBUILD_HOSTLDFLAGS 等) ...
# ... (设置目标 (内核) 工具链变量 CC, LD, AR, NM, RUSTC 等) ...
# 根据是否定义 LLVM 环境变量来选择 clang/lld 或 gcc/ld 工具链
# ... (设置 KBUILD 构建标志 KBUILD_CFLAGS, KBUILD_RUSTFLAGS, KBUILD_AFLAGS, KBUILD_CPPFLAGS) ...
# 包含大量的默认编译选项,如 -std=gnu11, -fno-common, Rust edition 等
# ... (根据内核配置 CONFIG_xxx 打开或关闭特定的编译/链接选项) ...
# 例如:优化级别 (-O2, -Os), 栈保护, Ftrace, LTO, CFI, GCOV, 调试信息, KASAN, UBSAN 等
# ... (包含特定功能的 Makefile 片段,如 scripts/Makefile.debug, scripts/Makefile.kasan) ...
# ... (添加用户自定义的标志 KCFLAGS, KRUSTFLAGS 等) ...
# ... (设置最终的链接器标志 LDFLAGS_vmlinux, KBUILD_LDFLAGS_MODULE) ...
# ... (核心目标定义) ...
ifeq ($(KBUILD_EXTMOD),) # 如果不是构建外部模块
# ... (定义内核构建需要的目标和依赖) ...
core-y:= # 内核核心对象
drivers-y:= # 驱动对象
libs-y:= # 库对象
export KBUILD_VMLINUX_OBJS # vmlinux 链接需要的目标文件列表
export KBUILD_VMLINUX_LIBS # vmlinux 链接需要的库文件列表
export KBUILD_LDS # 内核链接脚本路径
vmlinux.a: ... # 将各部分 .a 文件归档成 vmlinux.a
vmlinux: vmlinux.o ... modpost # 最终链接生成 vmlinux 文件
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.vmlinux
else # 如果是构建外部模块
# ... (外部模块的特定逻辑) ...
modules: modules_prepare
endif
# ... (定义 prepare 目标及其依赖) ...
# prepare0, prepare, archprepare: 在递归构建开始前需要完成的准备工作
# 例如:生成头文件、版本文件、检查配置同步、编译脚本等
# ... (定义头文件生成和安装目标 headers, headers_install) ...
# ... (定义安装目标 install, vdso_install) ...
# ... (定义内核工具构建目标 tools/, tools/%) ...
# ... (定义内核自测试目标 kselftest, kselftest-*) ...
# ... (定义设备树 (Devicetree) 相关目标 dtbs, dtbs_install, dtbs_check) ...
# 只在相关架构下有效
# ... (定义模块构建相关目标 modules, modules_install, modpost) ...
# modpost: 处理模块符号、生成 Module.symvers 等
# ... (定义清理目标 clean, mrproper, distclean) ...
# clean: 删除大部分生成文件,保留配置和外部模块构建所需文件
# mrproper: 删除所有生成文件和配置
# distclean: mrproper + 编辑器备份文件等
# ... (定义打包目标 %pkg, %src-pkg) ...
# ... (定义 help 目标,打印帮助信息) ...
# ... (定义静态分析和检查目标 includecheck, versioncheck, coccicheck, checkstack) ...
# ... (定义 Rust 相关目标 rustavailable, rustfmt, rustdoc, rusttest, rust-analyzer) ...
# ... (处理单一目标构建,如 make fs/ext4/ext4.o) ...
# ... (读取之前保存的目标命令缓存 .cmd 文件) ...
.PHONY: $(PHONY) # 声明所有 PHONY 目标
```
**总结来说,这个 Makefile 的核心功能是:**
1. **配置管理:** 处理内核配置选项 (`make *config`),生成配置文件 (`.config`, `auto.conf`, `autoconf.h`)。
2. **环境设置:** 设置编译所需的各种环境变量、工具链路径、编译和链接标志。
3. **依赖管理:** 定义了复杂的依赖关系,确保文件按正确的顺序编译和链接。
4. **递归构建:** 通过调用子目录中的 `Kbuild` 或 `Makefile` 文件来构建内核的各个子系统。
5. **目标构建:** 定义了各种构建目标,如:
* `vmlinux`: 构建核心内核镜像。
* `modules`: 构建可加载内核模块 (`.ko` 文件)。
* `dtbs`: 构建设备树文件。
* `headers_install`: 安装内核头文件供用户空间使用。
* `install`: 安装内核和模块。
* `clean`/`mrproper`/`distclean`: 清理构建生成的文件。
6. **工具链选择:** 支持 GCC 和 Clang/LLVM 工具链。
7. **语言支持:** 支持 C 和 Rust 语言的编译。
8. **外部模块支持:** 允许编译不在内核源码树中的模块。
9. **可定制性:** 提供了大量变量(如 `ARCH`, `CROSS_COMPILE`, `O`, `V`, `C`, `W`)供用户在命令行或环境 中设置,以定制构建过程。
10. **自动化与检查:** 集成了静态分析、格式检查、版本检查等工具。
理解这个 Makefile 需要对 `make` 工具的工作原理、C 语言编译链接过程以及 Linux 内核的模块化结构有深入的了解。它是 Kbuild 系统(Linux 内核的构建系统)的核心驱动文件。
猜你喜欢
- 2025-05-02 Linux基础运维篇:Linux系统监控工具(第015课)
- 2025-05-02 CentOS 7安装TCP BBR拥塞算法(centos7开启bbr加速)
- 2025-05-02 Emacs 折腾日记(十八)——改变Emacs的样貌
- 2025-05-02 Linux man 命令使用教程(linux man -k)
- 2025-05-02 qemu linux内核(5.10.209)开发环境搭建
- 2025-05-02 Emacs 折腾日记(十五)——窗口(emacs切换窗口)
- 2025-05-02 空间电推进技术概览及评述(连载之四)
- 2025-05-02 每天LINUX学习:Linux开启VLAN的支持及配置方法
- 2025-05-02 用云存储30分钟快速搭建APP,你信吗?
- 2025-05-02 图文详解SAMA5D2处理器在加载Linux内核前到底做了什么?
你 发表评论:
欢迎- 最近发表
-
- 10款鲜为人知的PHP框架(10款鲜为人知的php框架代码)
- 3分钟搞懂反弹shell(反弹shell的常用命令)
- 计算机专业必须掌握的脚本开发语言—shell
- shell 基本语法(shell基本语法set)
- 学习Shell 教程(shell编程学习)
- 一个有意思的PHP Webshell,利用伪协议执行代码
- Linux入门-shell编程-适合小白(linux shell编程是什么)
- GrayLog开源日志管理平台技术文章合集【共58篇】
- AI大模型 MiniMax 基于 Apache Doris 的日志系统,PB 级秒级查询响应
- 互联网大厂后端必看!手把手教你替换 Spring Boot 中的日志框架
- 标签列表
-
- jdk (81)
- putty (66)
- rufus (78)
- 内网穿透 (89)
- okhttp (70)
- powertoys (74)
- windowsterminal (81)
- netcat (65)
- ghostscript (65)
- veracrypt (65)
- asp.netcore (70)
- wrk (67)
- aspose.words (80)
- itk (80)
- ajaxfileupload.js (66)
- sqlhelper (67)
- express.js (67)
- phpmailer (67)
- xjar (70)
- redisclient (78)
- wakeonlan (66)
- tinygo (85)
- startbbs (72)
- webftp (82)
- vsvim (79)
本文暂时没有评论,来添加一个吧(●'◡'●)