马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
Makefile.lkm 语法解析与学习指南
一、Makefile.lkm 涉及的语法与符号详解
1. 变量定义与赋值
- :=(立即赋值)
定义变量并立即展开右侧表达式,比方:- BUILD_DIR := build # BUILD_DIR 值为 "build"
复制代码 - +=(追加赋值)
向变量追加新值,比方:- RTSDK_CFLAGS += -D__KERNEL__ # 在原有 CFLAGS 后添加宏定义
复制代码 - =(延迟赋值)
变量值在引用时才会展开,比方:- VAR = $(shell date) # 每次引用 VAR 时才会执行 date 命令
复制代码 2. 特殊变量与函数
- $(subst from,to,text)
字符串更换函数,比方:- $(subst $(ROOT_PATH)/,,src/root/module.c) # 输出 "src/module.c"
复制代码 - $(addprefix prefix,names)
为每个名称添加前缀,比方:- $(addprefix build/, a.o b.o) # 输出 "build/a.o build/b.o"
复制代码 - $(wildcard pattern)
通配符匹配文件,比方:- $(wildcard *.c) # 返回所有 .c 文件的列表
复制代码 - $(shell command)
实行 Shell 下令并返回结果,比方:- PWD := $(shell pwd) # 获取当前目录路径
复制代码 3. 条件判断
- ifneq (a,b) / ifeq (a,b)
比较两个值是否不等/相称,比方:- ifneq ($(KERNELRELEASE),) # 如果 KERNELRELEASE 非空
复制代码 - $(if condition,then-part,else-part)
条件函数,比方:- $(if $(wildcard dir),,mkdir dir) # 目录不存在则创建
复制代码 4. 规则与下令
- 目标-依赖规则
- target: dependency
- command # 命令必须以 Tab 开头
复制代码 示例:- modules: link_file_local
- $(MAKE) -C ... # 构建模块前先执行 link_file_local
复制代码 - .PHONY 伪目标
声明目标不代表实际文件,比方:- .PHONY: install clean # install 和 clean 是动作而非文件
复制代码 5. 通配符与自动变量
- %(通配符)
模式匹配,比方:- %.o: %.c # 所有 .o 文件依赖对应的 .c 文件
复制代码
<strong>$@、$ 函数 > 条件/循环。
- 实践建议:从单一文件编译开始,逐步扩展到多目次、多条件项目。
- $(foreach i,$(MODULE_SRC), ln -s $i ...) # 为每个源文件创建符号链接
复制代码 总结:Makefile.lkm 内容与 Linux 内核模块编译流程
一、Makefile.lkm 焦点内容解析
1. 变量定义
- 路径与编译选项:
- link_file_local:
- $(foreach i,$(MODULE_SRC), \
- $(shell ln -s ...)) # 使用 \ 换行
复制代码 2. 内核模块构建规则
- 条件分支:
- CC = gcc
- CFLAGS = -Wall
- OBJ = main.o utils.o
- app: $(OBJ)
- $(CC) $(CFLAGS) -o $@ $^
- %.o: %.c
- $(CC) $(CFLAGS) -c $<
复制代码 3. 符号链接与目次管理
- link_file_local 目标:
- # 生成符号链接到 build 目录
- link_file_local:
- $(foreach i,$(MODULE_SRC), \
- $(if $(wildcard $(dir $(BUILD_DIR)/...)),, \
- $(shell mkdir -p ...)) \
- $(shell ln -s $i ...))
复制代码 4. 安装与清理规则
- 安装:
- ################################################################################
- #
- #
- # Rules for building Linux Kernel Module
- ################################################################################
- BUILD_DIR := build
- LKM_INSTALL_PATH := $(ROOTFS_PATH)/lib/modules
- RTSDK_CFLAGS += -D__KERNEL__
- OUTPUT_MODULE_OBJ := $(addprefix $(BUILD_DIR)/,$(subst $(ROOT_PATH)/,,$(MODULE_OBJ)))
- EXTRA_CFLAGS += $(RTSDK_CFLAGS)
- EXTRA_LDFLAGS += $(RTSDK_LDFLAGS)
- ifneq ($(KERNELRELEASE),)
- obj-m := $(MODULE_NAME).o
- $(MODULE_NAME)-objs := $(OUTPUT_MODULE_OBJ)
- else
- PWD := $(shell pwd)
- modules: link_file_local
- $(MAKE) -C $(OSS_LINUX_PATH) $(MAKEOPT) M=$(PWD) ARCH=$(RTSDK_ARCH) "CROSS_COMPILE=$(CROSS_COMPILE)" "CC=$(CC)" "LD=$(LD)" "AR=$(AR)" modules
- endif
- .PHONY: install
- install:
- ifeq ($(MODULE_NAME).ko, $(wildcard $(MODULE_NAME).ko))
- $(if $(wildcard $(LKM_INSTALL_PATH)),,mkdir -p $(LKM_INSTALL_PATH))
- $(STRIP) --strip-debug -R .note -R .comment $(MODULE_NAME).ko
- cp $(MODULE_NAME).ko $(LKM_INSTALL_PATH)
- @echo "$(MODULE_NAME).ko installed"
- endif
- clean distclean clean_all:
- if [ -d $(BUILD_DIR) -a -d $(OSS_LINUX_PATH) ]; then $(MAKE) -C $(OSS_LINUX_PATH) M=$(PWD) "CC=$(CC)" "LD=$(LD)" "AR=$(AR)" clean; fi
- @find \( -name '*.[oas]' -o -name 'Module.symvers' -o -name 'modules.order' \) -type f -print | xargs rm -rf
- rm -rf $(BUILD_DIR)
- link_file_local:
- $(foreach i,$(MODULE_SRC),$(if $(wildcard $(dir $(BUILD_DIR)/$(subst $(ROOT_PATH),,$i))),,$(shell mkdir -p $(dir $(BUILD_DIR)/$(subst $(ROOT_PATH),,$i)))))
- $(foreach i,$(MODULE_SRC),$(if $(wildcard $(BUILD_DIR)/$(subst $(ROOT_PATH),,$i)),,$(shell ln -s $i $(BUILD_DIR)/$(subst $(ROOT_PATH),,$i))))
复制代码 - 清理:
- BUILD_DIR := build # 构建输出目录
- LKM_INSTALL_PATH := $(ROOTFS_PATH)/lib/modules # 模块安装路径
- RTSDK_CFLAGS += -D__KERNEL__ # 内核模块编译必选宏
- OUTPUT_MODULE_OBJ := $(addprefix $(BUILD_DIR)/,$(subst $(ROOT_PATH)/,,$(MODULE_OBJ))) # 对象文件路径映射
复制代码 二、Linux 内核模块编译流程
1. 准备环境
- 设置变量(在 Makefile 或下令行中):
- ifneq ($(KERNELRELEASE),) # 在内核构建系统中被调用时
- obj-m := $(MODULE_NAME).o
- $(MODULE_NAME)-objs := $(OUTPUT_MODULE_OBJ)
- else # 用户态执行时
- modules: link_file_local # 依赖符号链接创建
- $(MAKE) -C $(OSS_LINUX_PATH) M=$(PWD) ... modules # 调用内核构建系统
- endif
复制代码 2. 编译模块
- link_file_local:
- # 创建构建目录结构,并为源文件创建符号链接到 build/ 目录
- $(foreach i,$(MODULE_SRC), mkdir -p ... && ln -s ...)
复制代码 3. 安装模块
- install: # 安装内核模块到根文件系统
- $(STRIP) --strip-debug ... # 剥离调试符号
- cp $(MODULE_NAME).ko $(LKM_INSTALL_PATH)
复制代码 4. 清理构建文件
- clean distclean clean_all: # 删除构建产物
- make -C $(OSS_LINUX_PATH) clean # 清理内核构建中间文件
- rm -rf $(BUILD_DIR) ... # 删除本地构建目录
复制代码 三、常见问题与解决
问题现象原因与解决方案No such file or directoryOSS_LINUX_PATH 未定义或路径错误:在 Makefile 或下令行中指定正确路径。模块编译后无法加载内核版本与模块不匹配:确保 OSS_LINUX_PATH 指向与目标内核一致的源码目次。符号链接未正确生成link_file_local 实行失败:检查 MODULE_SRC 变量是否包含有效源文件路径。权限不足导致安装失败利用 sudo 或确保 LKM_INSTALL_PATH 对当前用户可写。四、完整编译流程图
- # 示例:Realtek SDK 环境变量
- export OSS_LINUX_PATH=/home/jiangzhiguo/SDK4_V4.4.1.55661/build/oss/linux-4.4.153
- export ARCH=arm64
- export CROSS_COMPILE=aarch64-linux-gnu-
复制代码 五、关键留意事项
- 路径一致性:确保 OSS_LINUX_PATH、ROOT_PATH 等变量与实际源码路径匹配。
- 内核配置:若修改内核选项(如添加驱动支持),需重新实行 make menuconfig 并编译内核。
- 版本匹配:模块与运行中的内核版本必须一致(通过 uname -r 查看)。
通过以上步调,可高效管理内核模块的构建、安装与维护。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
|