编译linux内核模块时的make -C M= modules的参数说明

在linux下编译可加载内核模块形成.ko文件的makefile中的核心语句是:

bash 复制代码
$(MAKE) -C $(KERNEL_DIR) M=(PWD) modules

这句是Makefile的规则:这里的

  • $(MAKE)就相当于make;
  • -C 选项的作用是指将当前工作目录转移到你所指定的位置,一般都是内核源代码目录或者内核headers目录,如/usr/include/linux-5.1.1-headers/类似的位置;-C $(KERNEL_DIR) 代表切换工作目录,因为内核源码顶层的Makefile文件定义了伪目标 modules,所以要先将工作目录切换到内核源码顶层 Makefile 所在位置;
  • "M="选项的作用是,当用户需要以某个内核为基础编译一个外部模块的话,需要在make modules 命令中加入"M=dir",程序会自动到你所指定的dir目录中查找模块源码,将其编译,生成KO文件。

注释:这个是编译linux核外模块必备的makefile中的核心语句,相当于先跳转到-C 指定的内核目录,然后执行这个目录下的makefile M=XXX modules(这个有点不好理解,一般都是在某个目录下执行makefile就行了,这里是执行makefile M=XXX modules),相当于在当前系统内核根目录下执行make M= XXX modules 进行XXX目录下的模块的编译,编译成xx.ko.这样你的modules就可以在任何其他位置,而不用必须在内核文件下面了。

例如:

bash 复制代码
obj-m := hello.o
KVERSION:= $(shell uname -r)

all:
	$(MAKE) -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
clean:
	$(MAKE) -C /lib/modules/$(KVERSION)/build M=$(PWD) clean

---------------------------------------------------我是分割线------------------------------------------------------------

新的(一般指>2.6.18的内核版本)内核模块编程中的make命令里有个M选项,如下:

bash 复制代码
make -C /lib/modules/$(shell uname -r )/build M = $(PWD) modules

M=$(PWD) 意思是返回到当前目录继续读入、执行当前的Makefile

请参考:从 2.4 到 2.6:Linux 内核可装载模块机制的改变对设备驱动的影响-CSDN博客

这个M是kbuild的东西呢,还是make本来自己就有的东西呢?

按理说,它是make的一个参数,应该是make的东西,但是make的doc里又找不到,如果是kbuild里的东西,它应该怎样来实现呢?经查证这个M是内核根目录下的Makefile中使用的变量。M是makefile脚本中的一个变量(variable):

Use make M=dir to specify directory of external module to build.

Old syntax make ... SUBDIRS=$PWD is still supported.

Setting the environment variable KBUILD_EXTMOD take precedence

bash 复制代码
ifdef SUBDIRS
    KBUILD_EXTMOD ?= $(SUBDIRS)
endif
ifdef M //如果没有定义或赋值M,此处M未定义(undefined)

    ifeq ("$(origin M)", "command line") //如果定义了,此句用来判断M是否从命令行来
        KBUILD_EXTMOD := $(M)
    endif
endif

以下是来自:从 2.4 到 2.6:Linux 内核可装载模块机制的改变对设备驱动的影响

清单3:2.6 内核模块的Makefile模板

bash 复制代码
#Makefile 2.6 
ifneq ($(KERNELRELEASE),) 
    #kbuild syntax. dependency relationshsip of files and target modules are listed here. 
    mymodule-objs := file1.o file2.o
    obj-m := mymodule.o
else
    PWD := $(shell pwd)
    KVER ?= $(shell uname -r) KDIR:=/lib/modules/$(KVER)/build
    all: 
        $(MAKE) -C $(KDIR ) M=(PWD) modules
    clean: rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions
endif

KERNELRELEASE是在内核源码的顶层Makefile中定义的一个变量,在第一次读取执行此Makefile时,KERNELRELEASE没有被定义, 所以make将读取执行else之后的内容。

如果make的目标是clean,直接执行clean操作,然后结束。

当make的目标为all时,-C $(KDIR ) 指明跳转到内核源码目录下读取那里的Makefile (解决M这个变量的定义问题,和KERNELRELEASE变量赋值问题); M=(PWD) 表明然后返回到当前目录继续读入、执行当前的Makefile。当从内核源码目录返回时,KERNELRELEASE已被被定义,kbuild也被启动去解析kbuild语法的语句,make将继续读取else之前的内容。else之前的内容为kbuild语法的语句, 指明模块源码中各文件的依赖关系,以及要生成的目标模块名。mymodule-objs := file1.o file2.o表示mymoudule.o 由file1.o与file2.o 连接生成。obj-m := mymodule.o表示编译连接后将生成mymodule.o模块。

补充一点,"(MAKE) -C ( KDIR ) M=(PWD)"与"(MAKE) -C (KDIR ) SUBDIRS=(PWD)"的作用是等效的,后者是较老的使用方法。推荐使用M而不是SUBDIRS,前者更明确。

通过以上比较可以看到,从Makefile编写来看,在2.6内核下,内核模块编译不必定义复杂的CFLAGS,而且模块中各文件依赖关系的表示简洁清晰。

针对要编译的模块的过程,我猜测应该是:(1)读取模块的makefile,因为没有定义KERNELRELEASE变量,所以先走else分支,获得KDIR等信息;(2)然后执行make all,发现需要走make -C (KDIR) M=(PWD) modules;(3)跳转到$(KDIR)目录,读取该目录下的makefile,得到KERNELRELEASE变量和M的定义和值,以及其他所需要的内容;(4)跳转到模块代码目录,再读取makefile,发现if已经成立,走if;(5)走make modules编译模块代码生成ko文件。至于为什么这样,参考linux KBuild MakeFile介绍 之二-CSDN博客

相关推荐
L_09072 分钟前
【Linux】进程状态
linux·开发语言·c++
啟明起鸣2 分钟前
【Nginx 网关开发】上手 Nginx,简简单单启动一个静态 html 页面
运维·c语言·前端·nginx·html
小生不才yz5 分钟前
shell编程 - 数据流指南
linux
卡里笔记6 分钟前
C语言版2048小游戏
c语言
lisanmengmeng11 分钟前
添加ceph节点
linux·服务器·ceph
Tinyundg14 分钟前
Linux系统分区
linux·运维·服务器
要做一个小太阳17 分钟前
华为Atlas 900 A3 SuperPoD 超节点网络架构
运维·服务器·网络·华为·架构
梵刹古音22 分钟前
【C语言】 循环结构
c语言·开发语言·算法
江畔何人初22 分钟前
service发现
linux·运维·云原生
life码农28 分钟前
Linux系统清空文件内容的几种方法
linux·运维·chrome