linux内核可加载模块的makefile

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

本篇文章中只讨论linux内核模块编译的makefile,linux内核makefile总览可以参考另一篇博客:linux内核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表示直接将模块编译进内核。

可以看到,这里并没有输入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在编译时将会进入内核源码目录,执行编译,编译完成时返回。

$KDIR:/lib/modules/$(shell uname -r)/build/,指定内核源码的位置。

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

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

M=$(PWD):需要编译的模块源文件地址


[target]:modules,事实上,这是个可选选项。默认行为是将源文件编译并生成内核模块,即module(s),但是它还支持一下选项:

  • modules_install:安装这个外部模块,默认安装地址是/lib/modules/$(uname -r)/extra/,同时可以由内建变量INSTALL_MOD_PATH指定安装目录
  • clean:卸载源文件目录下编译过程生成的文件,在上文的makefile最后一行可以看到。
  • 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目录添加到编译时的头文件搜索目录中。

linux内核makefile总览可以参考另一篇博客:linux内核makefile概览

好了,关于linux编译内核模块的makefile介绍就到此为止啦,如果朋友们对于这个有什么疑问或者发现有文章中有什么错误,欢迎留言

原创博客,转载请注明出处!

祝各位早日实现项目丛中过,bug不沾身.

linux内核模块编译makefile的更多相关文章

  1. Linux 内核模块编译 Makefile

    驱动编译分为静态编译和动态编译:静态编译即为将驱动直接编译进内核,动态编译即为将驱动编译成模块. 而动态编译又分为两种: a -- 内部编译 在内核源码目录内编译 b -- 外部编译 在内核源码的目录 ...

  2. Linux内核模块Makefile学习

    在<Linux设备驱动程序>一书中读到的内核模块编译Makefile,不是非常理解,在查询很多资料后,在这里做个总结. 书中Makefile代码: ifneq ($(KERNELRELEA ...

  3. [ARM-LInux开发]linux设备驱动makefile入门解析

    以下内容仅作参考,能力有限,如有错误还请纠正.对于一个普通的linux设备驱动模块,以下是一个经典的makefile代码,使用下面这个makefile可以完成大部分驱动的编译,使用时只需要修改一下要编 ...

  4. 关于linux内核模块Makefile的解析

    转载:http://www.embeddedlinux.org.cn/html/yingjianqudong/201403/23-2820.html Linux内核是一种单体内核,但是通过动态加载模块 ...

  5. linux/module.h: No such file or directory 内核模块编译过程

    1.缺少Linux kernel头文件 To install just the headers in Ubuntu: sudo apt-get install linux-headers-$(unam ...

  6. Linux环境下使用VSCode编译makefile文件的注意事项

    Linux环境下使用VSCode编译makefile文件的注意事项 首先安装C/C++的两个依赖 在debug,launch会自动的生成下方的launch.json launch.json { // ...

  7. linux如何编译安装新内核支持NTFS文件系统?(以redhat7.2x64为例)

    内核,是一个操作系统的核心.它负责管理系统的进程.内存.设备驱动程序.文件和网络系统,决定着系统的性能和稳定性.Linux作为一个自由软件,在广大爱好者的支持下,内核版本不断更新.新的内核修订了旧内核 ...

  8. 5.linux内核模块基础,内核模块学习

    linux内核模块基础 一.定义 Linux 内核的整体结构非常庞大,其包含的组件也非常多,如何使用这些组件呢: 方法 1:把所有的组件都编译进内核文件,即:zImage 或 bzImage,但这样会 ...

  9. Linux内核模块简介

    一. 摘要 这篇文章主要介绍了Linux内核模块的相关概念,以及简单的模块开发过程.主要从模块开发中的常用指令.内核模块程序的结构.模块使用计数以及模块的编译等角度对内核模块进行介绍.在Linux系统 ...

随机推荐

  1. 阶段一-01.万丈高楼,地基首要-第2章 单体架构设计与准备工作-2-27 为何不使用@EnableTransactionManagement就能使用事务?

    使用了注解使用事务.但是没有开启注解的启用 启动类里面使用注解 @EnableTransactionManager开启事物的管理. 为什么我们没有开启这个注解,还需要在响应的Service里面使用事务 ...

  2. docker批量删除镜像

    docker rmi `docker images | grep swb | grep -v grep | awk '{print $3}'` 参考: https://blog.csdn.net/hi ...

  3. Docker使用 - 容器

    查看容器 命令:docker  ps  [options] options有: -a:查看所有容器,包含不在运行中的(不带-a参数,是只显示运行中的容器) -q:只显示容器ID -s:多加一列来显示总 ...

  4. 深入浅出依赖注入容器——Autofac

    1.写在前面 相信大家对IOC和DI都耳熟能详,它们在项目里面带来的便利大家也都知道,微软新出的.NetCore也大量采用了这种手法. 如今.NetCore也是大势所趋了,基本上以.Net为技术主导的 ...

  5. Vmvare v12下搭建Ubuntu操作系统-18.04-desktop-amd64.iso镜像环境

    1.选择[典型],点击下一步.  2.找到ubuntu镜像文件所在位置,如图所示:  3.配置用户信息  4.设置安装位置  5.磁盘尽量大一些,笔者设置40g  6.自定义修改硬件信息  7.内存建 ...

  6. IDEA中类文件显示J,IDEA Unable to import maven project: See logs for details

    今天用了下lemon清理了下垃圾后,IDEA打开项目类文件图标由C变为J,在IDEA右侧的Maven Project中点击刷新提示IDEA Unable to import maven project ...

  7. Xcode7.2真机测试问题"The account 'appleID ' has no team with ID ‘’

     在Xcode7(测试版)提出免费真机测试的时候,我立刻在网上搜寻测试步骤,很简单,按照步骤走就可以. 但在7.2以后,突然我的iPhone不能真调了!提示"The account 'app ...

  8. 山峰和山谷 Ridges and Valleys

    题目描述 思路 一开始看这道题目,也不是很会,谁会把统计之类的问题和bfs联系在一起,没有开始的状态,没有结束的状态,题目中连一个最短之类的词也没有出现. 然后统计嘛,题目中说了方格高度都相同,就把周 ...

  9. Python中NumPy的使用一

    NumPy简介: 一个用python实现的科学计算,包括:1.一个强大的N维数组对象Array:2.比较成熟的(广播)函数库:3.用于整合C/C++和Fortran代码的工具包:4.实用的线性代数.傅 ...

  10. VMware Workstation 15 Player使用centos页面版本如何查看ip

    首先运行要使用的centos镜像,输入密码登陆进去 因为是界面版,所以就不需要再镜像中输入命令,但是因为这样又找不到没法用ifconfig查看ip怎么办?  这个就是类似于一个系统页面版本的linux ...