当前位置: 首页 > news >正文

Linux --- Makefile构建系统学习

嵌入式 Linux 驱动开发 - Makefile

目录

嵌入式 Linux 驱动开发 - Makefile

1. Makefile 基础

1.1 什么是 Makefile

1.2 Makefile 基本结构

2. 当前项目解析

2.1 项目结构

2.2 Makefile 逐行解析

2.3 自动变量详解

3. Makefile 核心语法

3.1 变量定义

3.2 模式规则

3.3 函数使用

3.4 条件判断

3.5 多文件项目示例

4. 内核模块 Makefile

4.1 最简单的内核模块 Makefile

4.2 参数解析

4.3 多文件内核模块

4.4 交叉编译(嵌入式开发)

5. 设备驱动开发实战

5.1 字符设备驱动示例

5.2 驱动 Makefile

5.3 使用流程

6. 常用技巧与最佳实践

6.1 目录结构组织

6.2 带子目录的 Makefile

6.3 条件编译

6.4 常用内核编译变量

6.5 完整项目模板

6.6 学习建议

6.7 常用命令参考


1. Makefile 基础

1.1 什么是 Makefile

Makefile 是一个文本文件,包含一系列规则,用于告诉 `make` 工具如何编译和链接程序。

1.2 Makefile 基本结构

makefile

目标:依赖 命令(必须是 Tab 缩进) target: dependencies command

1.3 运行 Make

make # 执行第一个目标 make clean # 执行 clean 目标 make -n # 预览命令,不执行 make -j4 # 并行编译(4 个任务)

2. 当前项目解析

2.1 项目结构
makefile_test/ └── 1-1/ ├── Makefile # 编译规则 ├── hello.c # 源代码 ├── hello.o # 目标文件(编译生成) └── hello # 可执行文件(链接生成)


2.2 Makefile 逐行解析


第 1 行:定义编译器

CC = gcc

第 2 行:定义编译选项

-Wall 开启所有警告 -g 生成调试信息 CFLAGS = -Wall -g

第 3 行:定义目标文件名

TARGET = hello

第 4 行:定义目标文件

OBJS = hello.o

第 6-7 行:链接规则

$@ 代表目标 (hello) $^ 代表所有依赖 (hello.o) $(TARGET): $(OBJS) $(CC) -o $@ $^

第 9-10 行:模式规则(通配符规则)

%.o 匹配所有 .o 文件 %.c 匹配对应的 .c 文件 $< 代表第一个依赖 %.o: %.c $(CC) $(CFLAGS) -c $< -o $@

第 12-13 行:清理目标

clean: rm -f $(TARGET) $(OBJS) 第 15 行:声明伪目标(不是真实文件) .PHONY: clean
2.3 自动变量详解

| 变量 | 含义 | 示例 |
|------|------|------|
| `$@` | 目标文件名 | `hello` |
| `$^` | 所有依赖文件 | `hello.o` |
| `$<` | 第一个依赖文件 | `hello.c` |
| `$?` | 比目标新的依赖 | - |
| `$*` | 匹配的模式部分 | `hello` (从 `hello.o`) |

3. Makefile 核心语法

3.1 变量定义


基本赋值

VAR = value # 延迟赋值(使用时展开) VAR := value # 立即赋值(定义时展开) VAR ?= value # 条件赋值(未定义时才赋值) VAR += value # 追加赋值

使用变量

$(VAR) 或 ${VAR}
3.2 模式规则


匹配所有 .c 文件编译为 .o

%.o: %.c $(CC) -c $< -o $@

多文件编译

SRCS = main.c func1.c func2.c OBJS = $(SRCS:.c=.o) # 替换扩展名
3.3 函数使用


获取所有源文件

SRCS := $(wildcard *.c)

替换扩展名

OBJS := $(patsubst %.c,%.o,$(SRCS))

查找文件

DIRS := $(shell find . -type d)

字符串操作

NAMES := $(notdir $(SRCS)) # 去掉目录 BASES := $(basename $(SRCS)) # 去掉扩展名
3.4 条件判断


判断变量

ifeq ($(CC),gcc) CFLAGS += -DGCC_COMPILER endif


判断文件是否存在

ifneq ($(wildcard config.h),) CFLAGS += -DHAVE_CONFIG endif # if-else ifdef DEBUG CFLAGS += -g -DDEBUG else CFLAGS += -O2 endif
3.5 多文件项目示例
CC = gcc CFLAGS = -Wall -g # 自动查找所有源文件 SRCS := $(wildcard *.c) OBJS := $(SRCS:.c=.o) TARGET = myapp $(TARGET): $(OBJS) $(CC) -o $@ $^ %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ clean: rm -f $(TARGET) $(OBJS) .PHONY: clean

4. 内核模块 Makefile

4.1 最简单的内核模块 Makefile
obj-m := hello.o KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) all: $(MAKE) -C $(KDIR) M=$(PWD) modules clean: $(MAKE) -C $(KDIR) M=$(PWD) clean .PHONY: all clean
4.2 参数解析

| 参数 | 含义 |
|------|------|
| `obj-m` | 指定要编译的内核模块(.o 文件) |
| `KDIR` | 内核源码目录 |
| `M=$(PWD)` | 外部模块目录 |
| `modules` | 内核编译目标 |

4.3 多文件内核模块
# 模块由多个文件组成 obj-m := mydriver.o mydriver-objs := main.o ops.o utils.o KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) all: $(MAKE) -C $(KDIR) M=$(PWD) modules clean: $(MAKE) -C $(KDIR) M=$(PWD) clean .PHONY: all clean
4.4 交叉编译(嵌入式开发)
obj-m := mydriver.o # 交叉编译工具链 CROSS_COMPILE := arm-linux-gnueabihf- ARCH := arm KDIR := /opt/kernel/linux-5.10 PWD := $(shell pwd) all: $(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) \ -C $(KDIR) M=$(PWD) modules clean: $(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) \ -C $(KDIR) M=$(PWD) clean .PHONY: all clean

5. 设备驱动开发实战

5.1 字符设备驱动示例

创建 `hello_driver.c`:

#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/uaccess.h> #define DEVICE_NAME "hello" #define BUFFER_SIZE 256 static char message[BUFFER_SIZE]; static int major_number; static int device_open(struct inode *inode, struct file *file) { printk(KERN_INFO "hello: Device opened\n"); return 0; } static int device_release(struct inode *inode, struct file *file) { printk(KERN_INFO "hello: Device closed\n"); return 0; } static ssize_t device_read(struct file *file, char __user *buf, size_t count, loff_t *offset) { int bytes_read = 0; // 实现读取逻辑 return bytes_read; } static ssize_t device_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) { // 实现写入逻辑 return count; } static struct file_operations fops = { .open = device_open, .release = device_release, .read = device_read, .write = device_write, }; static int __init hello_init(void) { major_number = register_chrdev(0, DEVICE_NAME, &fops); if (major_number < 0) { printk(KERN_ALERT "hello: Failed to register device\n"); return major_number; } printk(KERN_INFO "hello: Registered with major number %d\n", major_number); return 0; } static void __exit hello_exit(void) { unregister_chrdev(major_number, DEVICE_NAME); printk(KERN_INFO "hello: Unregistered\n"); } module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("Hello World Driver");
5.2 驱动 Makefile
obj-m := hello_driver.o KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) # 默认目标 all: modules modules: @echo "Building kernel module..." $(MAKE) -C $(KDIR) M=$(PWD) modules @echo "Build complete: $(PWD)/hello_driver.ko" clean: $(MAKE) -C $(KDIR) M=$(PWD) clean rm -f *.o *.ko *.mod.c *.mod *.order *.symvers install: $(MAKE) -C $(KDIR) M=$(PWD) modules_install depmod -a load: insmod hello_driver.ko unload: rmmod hello_driver dmesg: dmesg | tail -20 help: @echo "Makefile targets:" @echo " all - Build module (default)" @echo " clean - Remove build files" @echo " install - Install module" @echo " load - Load module" @echo " unload - Unload module" @echo " dmesg - Show kernel messages" .PHONY: all modules clean install load unload dmesg help
5.3 使用流程


# 1. 编译模块
make

# 2. 加载模块
sudo make load
# 或 sudo insmod hello_driver.ko

# 3. 查看设备
ls -l /dev/hello

# 4. 查看内核日志
make dmesg
# 或 dmesg | tail

# 5. 卸载模块
sudo make unload
# 或 sudo rmmod hello_driver

# 6. 清理
make clean

6. 常用技巧与最佳实践

6.1 目录结构组织


driver/
├── src/ # 源代码
│ ├── main.c
│ ├── ops.c
│ └── utils.c
├── include/ # 头文件
│ └── driver.h
├── Makefile # 编译规则
└── README.md # 说明文档

6.2 带子目录的 Makefile

```makefile
obj-m := mydriver.o
mydriver-objs := src/main.o src/ops.o

ccflags-y := -I$(src)/include

KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

all:
$(MAKE) -C $(KDIR) M=$(PWD) modules

clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean

.PHONY: all clean
```

6.3 条件编译
obj-m := mydriver.o # 调试版本 ifdef DEBUG ccflags-y += -DDEBUG -g endif # 架构相关 ifeq ($(ARCH),arm) ccflags-y += -DCONFIG_ARM endif KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) all: $(MAKE) -C $(KDIR) M=$(PWD) modules .PHONY: all


6.4 常用内核编译变量

| 变量 | 说明 |
|------|------|
| `ccflags-y` | 添加到 C 编译器的选项 |
| `asflags-y` | 添加到汇编器的选项 |
| `ldflags-y` | 添加到链接器的选项 |
| `subdir-m` | 进入子目录编译 |
| `obj-y` | 编译进内核的目标 |
| `obj-m` | 编译为模块的目标 |

6.5 完整项目模板

```makefile
#===========================================
# 模块配置
#===========================================
MODULE_NAME := mydriver
obj-m := $(MODULE_NAME).o
$(MODULE_NAME)-objs := main.o ops.o utils.o

#===========================================
# 编译配置
#===========================================
ARCH ?= $(shell uname -m)
ifeq ($(ARCH),x86_64)
ARCH := x86
endif

KDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

# 调试模式:make DEBUG=1
ifdef DEBUG
ccflags-y += -DDEBUG -g -O0
else
ccflags-y += -O2
endif

#===========================================
# 目标
#===========================================
all: modules

modules:
@echo "========================================"
@echo "Building $(MODULE_NAME).ko"
@echo "Architecture: $(ARCH)"
@echo "Kernel: $(shell uname -r)"
@echo "========================================"
$(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(PWD) modules
@echo "Build complete!"

clean:
@echo "Cleaning..."
$(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(PWD) clean
rm -f *.o *.ko *.mod.c *.mod *.order *.symvers
rm -f .*.cmd .*.flags .tmp_versions/*

install: modules
@echo "Installing module..."
$(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(PWD) modules_install
depmod -a
@echo "Installation complete!"

load:
@echo "Loading module..."
insmod $(MODULE_NAME).ko

unload:
@echo "Unloading module..."
rmmod $(MODULE_NAME)

reload: unload load

test:
@echo "Running tests..."
# 添加测试命令

help:
@echo "$(MODULE_NAME) Makefile"
@echo ""
@echo "Targets:"
@echo " all - Build module (default)"
@echo " clean - Remove build files"
@echo " install - Install module to system"
@echo " load - Load module"
@echo " unload - Unload module"
@echo " reload - Reload module"
@echo " test - Run tests"
@echo " help - Show this help"
@echo ""
@echo "Variables:"
@echo " DEBUG=1 - Build with debug symbols"
@echo " ARCH=arm - Cross-compile for ARM"
@echo " KDIR=... - Kernel source directory"

.PHONY: all modules clean install load unload reload test help
```

6.6 学习建议

1. **从简单开始**: 先理解当前项目的 Makefile
2. **动手实践**: 修改参数,观察变化
3. **阅读文档**: `make --help` 和 `man make`
4. **查看内核源码**: 学习内核自带的 Makefile
5. **循序渐进**: 用户空间 → 内核模块 → 完整驱动

6.7 常用命令参考

```bash
# Make 相关
make -n # 预览命令
make -B # 强制重新编译
make -j$(nproc) # 并行编译
make -C dir # 在指定目录执行

# 内核模块相关
modinfo xxx.ko # 查看模块信息
lsmod # 列出已加载模块
dmesg -w # 实时查看内核日志
```

---

http://www.jsqmd.com/news/494383/

相关文章:

  • Stable-Diffusion-v1-5-archive企业培训体系:内部AIGC工程师认证课程大纲
  • 864-批量word文档添加水印工具
  • Spring Boot+Nginx+MySQL容器化实战
  • Kimi-VL-A3B-Thinking镜像免配置优势:预编译vLLM、预下载模型权重、开箱即用
  • 七天速刷面试-day01
  • 2026年热门的南京摄影品牌推荐:南京商业摄影/南京食品摄影精选公司 - 品牌宣传支持者
  • QWEN-AUDIO实战案例:跨境电商多语种商品介绍语音批量生成
  • 如果 AI 能读懂并调用 LabVIEW,自动化系统会发生什么?
  • OpenClaw 超级 AI 实战专栏【数据与数据集】(一)高质量数据集:从哪找、怎么选、格式要求
  • Pi0 VLA仿真闭环:Web终端+Isaac Sim/Gazebo构建端到端训练验证环境
  • 1.1 模型量化简介:从动机、对象到主流方法全景
  • centos7系统安装教程
  • 2026携程酒店数据抓取
  • 2.1 模型剪枝(Model Pruning)
  • Ultrascale+ XDMA 从零开始搭建PCIE通信
  • 寻音捉影·侠客行精彩案例:某省级电视台用其日均处理300+小时新闻素材
  • 20260311 文本编辑器
  • 2026年靠谱的凸轮式自动车床工厂推荐:自动车床送料机实力厂家推荐 - 品牌宣传支持者
  • 自助游泳馆管理系统 vue3
  • 【一点浅思】Transformer架构是否已经触及性能天花板?未来架构突破的方向在哪里?
  • 零基础也能懂!OpenClaw 2026.3.8 (原Clawdbot)最全安装
  • SQL大师之路 02 MySQL架构介绍
  • 条码管理系统+WMS:物料入库扫码即建档,库存盘点1小时完成
  • C语言、结构体
  • Claude code底层实现原理(内存管理与并发)
  • C语言、自定义类型:联合体、枚举
  • DeepSeek LeetCode 699. 掉落的方块 public List<Integer> fallingSquares(int[][] positions)
  • GraphRAG开源生态全景:6大主流开源项目,微软/蚂蚁/港大项目同台PK
  • 软件综合项目笔记
  • 2026 最新解读:AI 在数字资产管理中的 5 大应用场景与实践路径