我用**"装修队"**的比喻来帮你彻底搞懂 Make 和 Makefile 的关系及定位。
1. 形象比喻:Make 和 Makefile 的关系
想象你要装修一套房子(编译一个驱动程序)。
- 源代码 (
.c) :是原材料(水泥、砖头、木板)。 - GCC (编译器) :是干活的工人(瓦工、木工)。他负责把原材料加工成成品。
- Make (命令) :是包工头/指挥官。他不亲自干活,他只负责指挥工人。
- Makefile (文件) :就是施工图纸/施工计划书。
它们的互动流程:
- 你(老板) 走到工地(终端目录),大喊一声:"开工!"(输入
make命令)。 - 包工头(Make) 听到命令,立刻拿起桌子上的 施工计划书(Makefile) 开始阅读。
- 计划书上写着:"先砌墙,再刷漆"。
- 包工头(Make) 就会转头指挥 工人(GCC):"哎,那个谁,先把这堆砖头砌起来!"
- 工人(GCC) 干完活,产出了 房子(驱动文件 .ko)。
结论:
- Make 是一个软件/工具。
- Makefile 是一个文本文件。
- Make 必须读懂 Makefile 里的规则,才知道怎么去调用编译器。
2. Makefile 在 Linux 驱动开发中的定位
它位于 "编写代码" 和 "生成驱动" 之间的核心环节。
流程对比:
- STM32 (Keil) 模式:
- 写代码 (
main.c) - 设置工程配置 (点开魔法棒图标,勾选芯片型号、头文件路径) -> 这其实就是在图形化地配置 Makefile
- 点"编译"按钮 -> IDE 自动处理
- 写代码 (
- Linux 驱动模式:
- 写代码 (
driver.c) - 写 Makefile (手动告诉编译器:内核在哪里?我要编译哪个文件?)
- 敲命令
make-> 生成driver.ko
- 写代码 (
它的定位:
它是连接你的驱动代码和 Linux 内核源码的桥梁。
因为驱动程序非常娇气,它不能独立存在,它必须依附于内核。Makefile 的作用就是告诉编译器:
"我要编译这个驱动,请你去 内核源码目录 借用一下那里的规则和头文件,把我的驱动编译成能装进那个内核的格式。"
3. 一个最简单的驱动 Makefile 解析
你刚才用 vi 修改的那个文件,内容大概是这样的。不要怕,每一行都有固定的套路:
Makefile
# 1. 定义变量:告诉 Make,内核源码在哪里 (这就是你刚才修改的地方)
KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88
# 2. 核心指令:告诉 Make 去哪里干活
all:
# make -C $(KERN_DIR) --> 意思是:Make,请你先穿越(Change directory)到内核目录里去。
# M=`pwd` --> 意思是:到了内核目录后,请记住(Module),要编译的代码其实在我当前这个目录(`pwd`)。
# modules --> 意思是:我要生成的是模块(modules),不是内核本身。
make -C $(KERN_DIR) M=`pwd` modules
# 3. 告诉 Make,要把哪个 .c 文件编译成 .o
obj-m += hello_drv.o
Makefile 并不是用 C 语言、Python 或其他通用编程语言写的。
它使用的是一种专门为 Make 工具设计的特定领域语言 (DSL - Domain Specific Language) ,但它的"肉体"其实是 Shell 脚本(Linux 命令)。
4. 总结
- Make 是那个执行命令的程序(包工头)。
- Makefile 是你写的规则文件(图纸)。
- 在 Linux 驱动开发中,Makefile 是必不可少的,因为我们需要通过它,利用
make -C的"穿越"功能,借用 Linux 内核庞大的构建系统来编译我们自己的小驱动。
下一步: 当你保存好 Makefile 后,直接在终端输入 make 并回车。如果一切顺利(没有报错),你会看到目录下多了一个 .ko 文件,那个就是最终的驱动程序了!