梦应逍遥 发表于 2025-4-20 09:58:31

makefile

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 '*.' -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企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: makefile