深度解析linux内核模块编译makefile

Linux内核模块Makefile深度解析:从基础语法到企业级工程实践 Makefile作为构建系统的核心配置文件,定义了软件编译、链接和安装的规则。在Linux内核开发领域,Kbuild系统通过一套标准化的Makefile语法和变量,实现了内核与模块的高效协同构建。其技术价值在于确保模块与内核版本的ABI兼容性,并支持跨版本、跨架构的复杂编译场景。对于内核模块开发者而言,掌握obj-m、KERNELDIR、ccflags-y等核心变量的正确用法,理解make -C与M=参数的委托编译机制,是解决编译失败、处理模块依赖和实现条件编译的关键。本文聚焦于Linux内核模块Makef 阅读详情
AI摘要

本文详细介绍了Linux内核开发中可加载模块的makefile编写,包括最简单的makefile示例,makefile中关键部分如obj-m和obj-y的解释,以及内核头文件和编译流程。重点强调了编写内核模块仅需内核头文件和Makefile,无需内核源码。

linux内核可加载模块的makefile

在开发linux内核驱动时,免不了要接触到makefile的编写和修改,尽管网上的makefile模板一大堆,做一些简单的修改就能用到自己的项目上,但是,对于这些基础的东西,更应该做到知其然并知其所以然。

linux内核使用的是kbuild编译系统,在编译可加载模块时,其makefile的风格和常用的编译C程序的makefile有所不同,尽管如此,makefile的作用总归是给编译器提供编译信息。

最简单的makefile

我们先来看看一个最简单的makefile是怎样的:

obj-m+=hello.o
    all:
            make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules
    clean:
            make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) clean

这个makefile的作用就是编译hello.c文件,最终生成hello.ko文件。

obj-m+=hello.o
obj-m表示编译生成可加载模块。相对应的,obj-y表示直接将模块编译进内核。obj-m和obj-y是GNU make 提供的扩展语法,并不是gcc的通用语法。

可以看到,这里并没有输入hello.c源文件,熟悉makefile的人应该知道,这得益于makefile的自动推导功能,需要编译生成filename.o文件而没有显示地指定filename.c文件位置时,make查找filename.c是否存在,如果存在就正常编译,如果不存在,则报错。

obj-m+=hello.o,这条语句就是显式地将hello.o编译成hello.ko,而hello.o则由make的自动推导功能编译hello.c文件生成。

all,clean
all,clean这一类的是makefile中的伪目标,伪目标并不是一个真正的编译目标,它代表着一系列你想要执行的命令集合,通常一个makefile会对应多个操作,例如编译,清除编译结果,安装,就可以使用这些伪目标来进行标记。在执行时就可以键入:

make clean
make install

等指令来完成相应的指令操作,当make后不带参数时,默认执行第一个伪目标的操作。

make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules
标准的make指令是这样的:make -C $KDIR M=$PWD [target],下面分别介绍每个字段的含义。

  • -C选项:此选项指定内核源码的位置,make在编译时将会进入内核源码目录,执行编译,编译完成时返回。在读取 Makefile 或执行任何其他操作之前更改为目录 dir。
  • $KDIR:/lib/modules/$(shell uname -r)/build/,指定内核源码(其实并不需要内核源码,需要的是目标机的内核头文件)的位置。

直接在目标板上编译时,内核头文件默认存放在/lib/modules/$(shell uname -r)/build/中,这个build/目录是一个软连接,链接到源码头文件的安装位置。而内核真正的源码库则直接引用正在运行的内核镜像。

 由上面我们可以知道make -C 就是到 /usr/src/linux-headers-5.13.0-52-generic提供的目录下去找Makefile.
那就让我们看看 /usr/src/linux-headers-5.13.0-52-generic目录下是啥:

该目录下的文件是内核头文件,注意这不是内核源码,同时我们也看到了内核头文件中的顶层Makefile,make实际执行的就是内核头文件的顶层 Makefile。

内核头文件下的各个目录基本都只有Kconfig和Makefile文件:

我们注意到每个内核版本都有一个带 -generic 和不带 -generic 的内核头文件,对于同一版本内核的这两个文件夹都只是头文件,并不是源代码。

带有 -generic 的目录里面均是指向不带 -generic 文件夹内容的符号链接:

注意:编写内核模块不需要内核源码,只需要内核头文件即可。
下载内核源码,上面我们看到/usr/src/目录下都只是不同版本的内核头文件,我们可以通过命令下载内核源码:sudo apt-get install linux-source
我这里下载 linux-source-5.4.0:sudo apt-get install linux-source-5.4.0

删除源码:

sudo apt-get remove linux-source-5.4.0

当跨平台编译时,就需要指定相应的内核源码目录,而不是系统中的源码目录,但是交叉编译时,需不需要指定架构平台和交叉编译工具链呢?我们接着往下看;

  • M=$(PWD):需要编译的模块源文件地址,M= 选项使 makefile 在试图建立模块目标前, 回到你的模块源码目录。
  • [target]:modules,事实上,这是个可选选项。默认行为是将源文件编译并生成内核模块,即module(s),但是它还支持以下选项:
  1. modules_install:安装这个外部模块,默认安装地址是/lib/modules/$(uname -r)/extra/,同时可以由内建变量INSTALL_MOD_PATH指定安装目录
  2. clean:卸载源文件目录下编译过程生成的文件,在上文的makefile最后一行可以看到。
  3. help:帮助信息

更多选项

编译多个源文件

hello_world总是简单的,但是在实际开发中,就会出现更复杂的情况,这时候就需要了解更多的makefile选项:

首先,当一个.o目标文件的生成依赖多个源文件时,显然make的自动推导规则就力不从心了(它只能根据同名推导,比如编译filename.o,只会去查找filename.c),我们可以这样指定:

obj-m  += hello.o
    hello-y := a.o b.o hello_world.o

hello.o目标文件依赖于a.o,b.o,hello_world.o,那么这里的a.o和b.o如果没有指定源文件,根据推导规则就是依赖源文件a.c,b.c,hello_world.c.
除了hello-y,同时也可以用hello-objs,实现效果是一样的。

同时编译多个可加载模块
kbuild支持同时编译多个可加载模块,也就是生成多个.ko文件,它的格式是这样的:

obj-m := foo.o bar.o
    foo-y := <foo_srcs>
    bar-y := <bar_srcs>

就是这么简单。

ifneq ($(KERNELRELEASE),)
通常,标准的makefile会写成这样:

ifneq ($(KERNELRELEASE),)
    obj-m  := hello.o
else
    KDIR ?= /lib/modules/`uname -r`/build
 
    all:
            $(MAKE) -C $(KDIR) M=$(PWD) modules
    clean:
            $(MAKE) -C $(KDIR) M=$(PWD) clean
endif

为什么要添加一个ifneq,else,all条件判断。

这得从linux内核模块make执行的过程说起:当键入make时,make在当前目录下寻找makefile并执行,KERNELRELEASE在顶层的makefile中被定义,所以在执行当前makefile时KERNELRELEASE并没有被定义,走else分支,直接执行。

$(MAKE) -C $(KDIR) M=$(PWD) modules
而这条指令会进入到$(KDIR)目录,调用顶层的makefile,在顶层makefile中定义了KERNELRELEASE变量.

在顶层makefile中会递归地再次调用到当前目录下的makefile文件,这时KERNELRELEASE变量已经非空,所以执行if分支,在可加载模块编译列表添加hello模块,由此将模块编译成可加载模块放在当前目录下。

归根结底,各级子目录中的makefile文件的作用就是先切换到顶层makefile,然后通过obj-m在可加载模块编译列表中添加当前模块,kbuild就会将其编译成可加载模块。

如果是直接编译整个内核源码,就省去了else分支中进入顶层makefile的步骤。

需要注意的一个基本概念是:每一次编译,顶层makefile都试图递归地进入每个子目录调用子目录的makefile,只是当目标子目录中没有任何修改时,默认不再进行重复编译以节省编译时间。

这里同时解决了上面的一个疑问:既然是从顶层目录开始编译,那么只要顶层目录中指定了架构(ARCH)和交叉编译工具链地址(CROSS_COMPILE),各子目录中就不再需要指定这两个参数。

头文件的放置
当编译的目标模块依赖多个头文件时,kbuild对头文件的放置有这样的规定:

  • 直接放置在makefile同在的目录下,在编译时当前目录会被添加到头文件搜索目录。
  • 放置在系统目录,这个系统目录是源代码目录中的include/linux/。
  • 与通用的makefile一样,使用-I$(DIR)来指定,不同的是,代表编译选项的变量是固定的,为ccflag.

一般的用法是这样的: ccflags-y := -I$(DIR)/include kbuild就会将$(DIR)/includ目录添加到编译时的头文件搜索目录中。

总结

编写内核模块不需要内核源码,只需要内核头文件(和相应的Makefile)即可。
如果要开发内核,则需要修改内核源码。


参考链接:https://blog.youkuaiyun.com/m0_74282605/article/details/128114212

参考链接:https://blog.youkuaiyun.com/weixin_45030965/article/details/124413885

Linux内核模块Makefile编写指南:从Kbuild原理到实战配置 Makefile是Linux内核开发中的核心构建配置文件,它定义了模块的编译规则和依赖关系。其工作原理基于内核的Kbuild系统,通过声明式变量(如obj-m)告知构建系统如何将源代码编译为可加载的内核模块(.ko文件)。理解Makefile对于确保模块与内核版本兼容性、管理符号导出和优化编译过程至关重要。在嵌入式开发、设备驱动编写等应用场景中,掌握Makefile的编写能有效解决模块加载错误、符号未定义等常见问题。本文结合Kbuild系统原理和工程实践,深入解析了如何编写高效、健壮的内核模块Makefil 阅读详情

相关推荐

Linux内核Makefile与Kbuild系统解析

Makefile作为Linux内核构建的核心工具,通过自动化编译规则实现高效的项目管理。其核心原理包括变量赋值、文件包含、自动推导等机制,结合Kbuild系统形成完整的构建框架。Kbuild系统采用模块化设计,通过顶层Makefile和scripts目录下的子Makefile协同工作,显著提升大型项目的构建效率。在Linux内核开发中,Makefile与Kbuild的深度整合支持从内核模块编译到完整系统镜像生成的各类场景,特别是通过$(build)和$(if_changed)等关键函数实现精确的增量构建。理

weixin_42388898的博客 598

内核模块编译方法

新功能源码与内核其它代码一起编译进uImage文件内。

zxd_yyds的博客 2244

RK3568开发中交叉编译器的配置与内核模块编译指南

交叉编译器是嵌入式开发中的关键工具,它允许开发者在x86架构的PC上为ARM架构的目标设备生成可执行代码。其核心原理是通过特定的工具链将高级语言转换为目标架构的机器码,解决了异构平台间的开发障碍。在RK3568这类Cortex-A55核心的ARM开发板上,正确配置交叉编译器环境尤为重要,需要确保工具链支持ARMv8-A架构并与内核版本匹配。技术价值体现在提升开发效率、保证代码兼容性以及优化资源利用。典型应用场景包括内核模块开发、驱动编写等底层软件开发。本文以iTOP-RK3568开发板为例,详细解析gcc-

458

一个简单的内核模块Makefile语法讲解

主要讲解了一个简单的内核模块的Makefile语法

小立仔 5439

编译Linux内核模块时Makefile的编写方法

在编写Linux内核模块时,Makefile是一个关键的文件,它用于组织和管理源代码的编译过程。目标是我们希望生成的文件,依赖是生成目标所需要的文件或其他目标。对于编译内核模块,我们通常需要指定模块的源文件和内核的头文件作为依赖。在Makefile中,我们定义了目标和依赖关系,并指定了编译规则。Makefile会调用内核的Makefile执行清理操作,删除生成的中间文件和目标模块。规则用于清理生成的文件,同样调用内核的Makefile并传递相应选项。命令,Makefile会自动编译源代码并生成目标模块。

ByteJolt的博客 549

驱动开发系列12 - Linux 编译内核模块的Makefile解释

6. 系统启动时自动加载 , 将模块名添加到 /etc/modules 文件中。5. 自动安装模块和依赖 sudo modprobe helloworld。1. 安装内核模块 sudo insmod helloworld.ko。7. 获取内核模块信息 modinfo helloworld.ko。2. 查看内核模块 lsmod |grep helloworld。4. 卸载内核模块 sudo rmmod helloworld。3. 查看内核日志 dmesg。8. 调试内核模块 printk。

GPU全栈博主 881

Makefile魔法:揭秘内核模块交叉编译的元编程艺术

本文深入探讨Makefile在Linux内核模块交叉编译中的元编程艺术,通过条件判断、宏扩展和函数递归等高级特性,实现单一Makefile自适应多种ARM32设备和内核版本。文章详细介绍了与Kbuild系统的深度集成策略,以及构建工业级自动化编译产线的实现方法,显著提升嵌入式开发效率。

flink9streamer的博客 888

Linux模块文件编译到内核与独立编译成.ko文件的方法

很多粉丝在群里提问,如何把一个模块文件编译到内核中或者独立变异成ko文件。本文给大家详解讲解。

嵌入式技术开发 6312

Linux 内核模块编程深度解析

动态加载:不需要重新编译内核动态卸载:不需要重启系统代码隔离:模块代码与内核代码分离// 模块初始化函数\n");return 0;// 模块退出函数\n");// 模块注册// 模块信息Linux 内核的模块机制是一个灵活而强大的系统,它不仅为系统提供了扩展功能,还为开发者提供了方便的开发环境。作为创业者,深入理解内核模块机制,不仅可以帮助我们开发更好的产品,还可以为技术选型和成本控制提供依据。

jiang_style的博客 2718

别再只用CANape测数据了!手把手教你用ASAP2 Studio编辑A2L文件(附MAP文件更新避坑指南)

本文深入探讨ASAP2 Studio在ECU开发中的高阶应用,特别是如何利用其编辑A2L文件并同步更新MAP文件,提升测量标定效率。文章详细解析了ASAP2 Studio的双向同步能力、MAP文件更新全流程及变量管理技巧,帮助工程师避免常见错误,优化工作流程。

weixin_26833939的博客 506

无感无刷电机控制进阶:反电动势过零检测的5种实现方案对比

本文深入探讨无感无刷电机控制中反电动势过零检测的5种实现方案,包括电阻分压虚拟中性点法、专用BEMF检测IC方案、基于ADC采样的数字锁相环、无传感器观测器技术及混合方案创新实践。通过对比分析各方案的优劣及适用场景,为电机控制工程师提供实用的技术选型指南,助力提升系统性能与成本效益。

weixin_28691741的博客 842

内核编译方法:从 make 到 bzImage——深度解析 Linux 根 Makefile 与 Kbuild 构建流程

当你在 Linux 内核源码树根目录敲下 `make` 时,背后究竟发生了什么?本文以 Linux 内核根 `Makefile` 为脉络,完整追踪内核构建的每一步:从版本变量定义、命令行参数解析、构建目录准备,到 `vmlinux` 的链接、`System.map` 的生成,再到最终 `bzImage` 压缩镜像的封装。通过阅读本文,你将理解 Kbuild 构建系统的工作方式、`built-in.

gitblog_00532的博客 791

【新华三】DHCP+TFTP+ZTP自动开局实战

设备运行ZTP功能,可以自动获取并自动加载开局文件,实现设备的免现场配置和部署,从而降低人力成本,提升部署效率。使用TFTP服务器工具进行文件的上传和下载,网上很多TFTP server软件,大家可以自行下载。3CDaemon等,可以根据使用自取所需。大家应该都了解ZTP,但是目前发现除了控制器之外仅使用ZTP开局的兄弟并不是很多,因此分享此过程方便大家批量开局,留出更多时间学习更多技术。电脑打开DHCP服务器,TFTP服务器,并通过网线连接到设备的管理口,若设备较多可以通过带外交换机等进行连接,

五大连池的镜子 4978

【Linux常犯错误解析】 Makefile痛点指南!不小心强制退出Makefile怎么办、秒懂C文件编译

本文介绍了Linux下使用Makefile编译C代码的实用技巧。通过图文结合的方式,详细讲解了Makefile的基本结构、编译原理和增量编译机制。文章从手动编译的痛点出发,逐步解析Makefile的"三段式"写法(目标、依赖、命令),并演示了如何通过时间戳对比实现智能增量编译。针对常见问题如.swp文件报错、多文件管理等提供了解决方案,帮助读者快速掌握Makefile的核心用法,提升C语言项目开发效率。

欢迎来到我的博客,私信我可以一起交流技术 4555

真格量化——GFTD策略

#!/usr/bin/env python # coding:utf-8 from PoboAPI import * import datetime import numpy as np import math #开始时间,用于初始化一些参数 def OnStart(context) : print "I\'m starting..." #设置全局变量 g.signalcount = 0 g.signal_list = [] g.buysignal = 0 .

qq_26742269的博客 1284

交通仿真软件:SUMO_(13).Python脚本与TraCI接口

通过TraCI接口,用户可以实现与SUMO仿真器的实时交互,获取和控制交通数据。本文档详细介绍了如何使用Python脚本与TraCI接口进行交互,包括基本的设置、数据获取和控制方法。此外,还展示了几个高级应用示例,如交通流优化、多代理系统、车辆路径规划和车辆行为模拟。这些示例可以帮助用户更好地理解如何利用TraCI接口进行复杂的交通仿真和分析。希望这些内容对您有所帮助,如果您有任何问题或需要进一步的帮助,请参考SUMO的官方文档或社区支持。

kkchenjj的博客 989

深度解析Linux中的make/Makefile的使用方法

进行修饰操作的话,那么我们的程序编译总是可以被执行的,因为我们忽略了这个对比时间,不会因为我们的源文件比可执行文件的修改时间早而使我们的文件不会进行编译操作。我们要生成的文件是Mytest文件,依赖的是test.o文件,但是现在我们当前目录中并不存在这个文件,那么我们就往下面去找了。因为我们的test.c没有被修改,我们的可执行程序的源文件没有进行任何的修改,那么就没有重新编写的必要了。如果我们单纯的想要我们的这里不出现我们的回显内容,我们只是将我们的结果打印出来。

2301_80863610的博客 2198

YOLO11涨点优化:卷积魔改 | DCNv2升级版本,助力检测

在DCN的基础上,增加了2个创新点,分别是调制模块和使用多个调制后的DCN模块,从形成了DCN的升级版本——DCNv2

①答疑群聊服务;②YOLO大模型知识问答系统;③计算机视觉论文生成智能体; 967

【Linux操作系统10】Makefile深度解析:从依赖推导到有效编译

本文深入剖析了Linux开发中Makefile的核心工作机制。文章从为什么需要Makefile出发,通过生活化的类比帮助读者理解"依赖关系"与"依赖方法"这两个核心概念。重点讲解了Makefile推导过程的栈结构原理——当依赖文件不存在时,make如何将命令入栈,找到源文件后再依次弹栈执行。文章还详细阐述了.PHONY伪目标的作用,以及Linux文件ACM时间(Access、Modify、Change)的概念和区别,揭示了Makefile判断文件是否需要重新编译的内在逻辑。

huanghaocheng30的博客 593

python实现DoIP基本通信(收发报文)

python实现DoIP基本通信(收发报文)

天赐好车的博客 894

嵌入式系统开发笔记101:STM32CubeIDE安装过程中的2个错误及其解决方法

STM32CubeIDE在安装过程中,遇到了2个错误,本文阐述其解决方法。产生上述错误的原因,都在于中文。STM32CubeIDE不能很好的支持中文,所以在使用STM32CubeIDE进行项目设计的过程中,不要使用中文,包括项目所在的路径。

snmplink的博客 8549

linux内核makefile分析

  本文简单介绍了 Linux 内核 Makefile 的大框架,对于KBuild 系统的详细的编译过程没有作过多的分析,写的很粗犷~还请见谅。 更详细的内容请参考:         嵌入式Linux应用开发完全手册         makefile.txt 这个可以参考  http://blog.youkuaiyun.com/lizuobin2/article/details/51447338       ...

fy198796的博客 1004

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

linux源码Makefile的详细分析

转载自:http://www.cnblogs.com/amanlikethis/p/3675486.html linux源码Makefile的详细分析 目录 一、概述   1、本文的意义   2、Linux内核Makefile文件组成 二、Linux内核Makefile的“make解析”过程   1 顶层Makefile阶段     1、从总目标uImage说起   

Second的专栏 3006

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

NVIDIA开源GPU内核模块:深度解析Linux显卡驱动的架构奥秘

你是否曾经遇到过在Linux系统上安装NVIDIA显卡驱动时遇到的各种兼容性问题?是否对闭源驱动的"黑盒"特性感到困扰?NVIDIA开源GPU内核模块项目正是为了解决这些问题而生。这个项目为Turing及更新架构的NVIDIA GPU提供了完整的Linux内核模块源代码,让开发者能够深入了解GPU驱动的工作原理并进行定制化开发。 ## 痛点切入:闭源驱动的局限性 传统的NVIDIA闭源驱动虽然

gitblog_01156的博客 1017

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

深度解析NVIDIA开源GPU内核模块:组件分离架构的终极指南

NVIDIA Linux open GPU kernel modules是NVIDIA发布的开源GPU内核模块源代码,它为Linux系统提供了对NVIDIA GPU的底层支持,是GPU与操作系统交互的关键桥梁。 ## 核心模块概览:了解GPU内核的构成 该项目包含多个核心模块,共同协作实现GPU的各项功能。其中,nvidia模块是核心部分,负责GPU的基本控制和管理,相关代码可在[kernel

gitblog_00738的博客 903
上一篇: 编译出错,出现 vfs_read[******.ko] undefined !
下一篇: Linux kernel 分析之一:内核镜像/映像
土豆西瓜大芝麻
博客等级 码龄18年 1189粉丝 238原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值