news 2026/8/29 8:02:31

【内核驱动基础】内核模块的两种编译方式(in-tree vs out-of-tree)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【内核驱动基础】内核模块的两种编译方式(in-tree vs out-of-tree)

目录

一、内核模块的编译方式(in-tree vs out-of-tree)

二、内核树外编译(out-of-tree)

特点

典型做法

适用场景

三、内核树内编译(in-tree)

参考资料


一、内核模块的编译方式(in-tree vs out-of-tree)

通常而言,内核模块代码有两种常见的构建形态:

  1. 内核树内编译(in-tree):把模块源码放进(或集成进)内核源码树,由内核顶层构建系统统一编译。
  2. 内核树外编译(out-of-tree,外部模块):模块源码独立放在内核树之外,通过make -C <kernel_dir> M=<module_dir> modules借用内核 Kbuild 来编译。

二、内核树外编译(out-of-tree)

特点

  • 模块源码独立,不需要进入内核源码树。

  • 仍然使用内核的 Kbuild 规则来编译,关键命令是:

    make -C <KERNEL_DIR> M=<MODULE_DIR> modules

典型做法

  • 模块目录里只放一个“外部模块 Makefile”
    • obj-m := hellomodule.o
    • make -C $(KERNEL_DIR) M=$(CURDIR) modules

适用场景

  • 驱动/模块开发调试(迭代快)
  • 第三方闭源或单独交付(不改内核树)
  • 同一内核版本下给多个项目复用

在内核树外编译的方式在我们之前的文章中其实已经实现了,可以参考【内核驱动基础】超详细一文详解Linux驱动模块这一文章,主要实现就是Makefile文件的编写

三、内核树内编译(in-tree)

在内核源码的文件目录下,我们可以看到有很多的驱动程序,我们通过drivers/char中的Makefile文件进一步举例说明,其位置如下:

Makefile文件的内容如下:

# SPDX-License-Identifier: GPL-2.0 # # Makefile for the kernel character device drivers. # obj-y += mem.o random.o obj-$(CONFIG_TTY_PRINTK) += ttyprintk.o obj-y += misc.o obj-$(CONFIG_ATARI_DSP56K) += dsp56k.o obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o obj-$(CONFIG_RAW_DRIVER) += raw.o obj-$(CONFIG_MSPEC) += mspec.o obj-$(CONFIG_UV_MMTIMER) += uv_mmtimer.o obj-$(CONFIG_IBM_BSR) += bsr.o obj-$(CONFIG_PRINTER) += lp.o obj-$(CONFIG_APM_EMULATION) += apm-emulation.o obj-$(CONFIG_DTLK) += dtlk.o obj-$(CONFIG_APPLICOM) += applicom.o obj-$(CONFIG_SONYPI) += sonypi.o obj-$(CONFIG_HPET) += hpet.o obj-$(CONFIG_XILINX_HWICAP) += xilinx_hwicap/ obj-$(CONFIG_NVRAM) += nvram.o obj-$(CONFIG_TOSHIBA) += toshiba.o obj-$(CONFIG_DS1620) += ds1620.o obj-$(CONFIG_HW_RANDOM) += hw_random/ obj-$(CONFIG_PPDEV) += ppdev.o obj-$(CONFIG_NWBUTTON) += nwbutton.o obj-$(CONFIG_NWFLASH) += nwflash.o obj-$(CONFIG_SCx200_GPIO) += scx200_gpio.o obj-$(CONFIG_PC8736x_GPIO) += pc8736x_gpio.o obj-$(CONFIG_NSC_GPIO) += nsc_gpio.o obj-$(CONFIG_GPIO_TB0219) += tb0219.o obj-$(CONFIG_TELCLOCK) += tlclk.o obj-$(CONFIG_MWAVE) += mwave/ obj-y += agp/ obj-$(CONFIG_PCMCIA) += pcmcia/ obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o obj-$(CONFIG_TCG_TPM) += tpm/ obj-$(CONFIG_PS3_FLASH) += ps3flash.o obj-$(CONFIG_XILLYBUS) += xillybus/ obj-$(CONFIG_POWERNV_OP_PANEL) += powernv-op-panel.o obj-$(CONFIG_ADI) += adi.o

这个文件的主要作用就是告诉内核的构建系统(Kbuild)在“字符设备驱动”这个目录下,需要编译哪些源文件/子目录,以及它们是编进内核(built-in)还是编成模块(.ko)。

典型写法是这种:

obj-$(CONFIG_FOO) += foo.o

含义:

  • CONFIG_FOO=yfoo.o编进内核镜像(built-in)
  • CONFIG_FOO=mfoo.o会被链接成foo.ko(模块)
  • CONFIG_FOO=n→ 不编译

那么问题来了,这些变量在哪里被设置呢?

—答:在内核根目录.config文件中进行配置,在我们make板卡厂家给的默认配置时,会默认配置.config文件

截取部分.config文件如下:

我们继续提问,这个.config又如何去修改里面的配置参数呢?当然,如果你要手动编写也是无可厚非的,但问题是内核有成千上万个配置选项(CONFIG_*),所以,内核引入了menuconfig。

这个.config文件是由各个驱动目录下的Kconfig文件汇总得到的,只需要修改驱动目录下对应的Kconfig文件,使用make menuconfig可以快速配置.config文件的参数内容。

menuconfig 是 Linux 内核 Kconfig 配置系统提供的一个交互式(终端界面)配置工具。它的核心目的很简单:让你用“菜单”的方式生成/修改内核的 .config,从而决定内核和各类驱动/子系统到底编译进内核(y)、编译成模块(m)还是不编译(n)。其界面如下:

在执行make menuconfig指令的时候一定要确定好自己的编译平台和交叉编译链工具是否正确,否则在部署到板卡上后很有可能会产生错误。例如在rk3588上是如下配置的:

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig

menuconfig常用的快捷键如下

  • /:搜索配置项(强烈推荐)
    • 输入关键字或CONFIG_名称,能直接定位选项所在菜单
  • ?:查看当前选项帮助信息(help)
  • y / n / m:直接把当前项设为 y/n/m
  • Space:循环切换状态(n → m → y)
  • Esc:返回/退出

技巧:找不到某个驱动选项时,先按 / 搜它,再根据搜索结果跳转到对应菜单。

下面我们使用一个实例进行强化:

我们当前完成了一个简单驱动程序模块:

#include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> static int __init hello_init(void) { printk(KERN_EMERG "[ KERN_EMERG ] Hello Module Init\\n"); printk( "[ default ] Hello Module Init\\n"); return 0; } static void __exit hello_exit(void) { printk("[ default ] Hello Module Exit\\n"); } module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE("GPL2"); MODULE_AUTHOR("cc "); MODULE_DESCRIPTION("hello world module"); MODULE_ALIAS("test_module");

我们将程序放入内核drivers/char目录下

在当前目录下的Makefile文件中,我们配置模块参数

在当前目录的Kconfig文件中,编写模块的Kconfig信息

此处的状态可以设置为bool(y或n)或者tristate(三态,y、n、m,m表示为一个模块)

然后在内核根目录执行make menuconfig,使用/搜索HELLO_MODULE可以看到如下:

在.config文件中搜索HELLO_MODULE可以看到参数已经配置成y

如果想要配置成默认为n,也可以在menuconfig中配置

至此,介绍完了两种编译方式,大家可以自行尝试加深理解。

参考资料

  • Linux下menuconfig与Kconfig基础知识概要
  • 韦东山Linux驱动课程
  • 内核参考文档
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/29 4:42:21

实测缩短70%课件制作时间:这款AIPPT工具就是老师的大救星

实测缩短70%课件制作时间&#xff1a;ChatPPT引领教学效率革命 实测数据显示&#xff0c;使用ChatPPT制作课件&#xff0c;基础构建时间可缩短70%以上&#xff0c;这不是夸张&#xff0c;而是众多教育工作者的真实反馈。在2026年的今天&#xff0c;课件制作正经历着一场由AI驱动…

作者头像 李华
网站建设 2026/8/21 12:37:43

CoDeSys入门实战一起学习(二十):布尔、整型、实数、字符串、时间5大类标准数据类型详解(附实战案例)

写CoDeSys程序的第一步&#xff0c;必然是声明变量/常量&#xff0c;而所有声明都离不开数据类型。CoDeSys的标准数据类型完全遵循IEC61131-3标准&#xff0c;共分为布尔、整型、实数、字符串、时间5大类&#xff0c;是所有PLC程序的“基础积木”。很多新手容易踩坑&#xff1a…

作者头像 李华
网站建设 2026/8/27 10:59:55

KingbaseES数据库瓶颈排查实战指南:从实例到语句的全维度解析

在高并发、海量数据的业务场景下&#xff0c;数据库性能直接决定了应用系统的响应速度和稳定性&#xff0c;而瓶颈排查则是性能调优的核心前提——只有精准定位问题根源&#xff0c;才能避免盲目调参、优化无效的内耗。KingbaseES作为国产数据库中的优秀代表&#xff0c;在政务…

作者头像 李华
网站建设 2026/8/22 14:43:43

巡防勤务可视化管理

巡防勤务可视化管理 巡防勤务管理可视化系统&#xff0c;基于大数据平台的警务地理信息系统&#xff0c;可以实时查看警力在岗状态、警力分布、应急资源等。系统支持快速定位警员、车辆的位置&#xff0c;查看警力详细信息&#xff0c;调取监控视频画面&#xff0c;并进行单方…

作者头像 李华
网站建设 2026/8/13 21:19:58

想把网页保存成PDF文件,快速删掉侧边栏广告再打印

想要把网页保存成PDF文件的时候经常会有右侧左侧侧边栏挡住主要内容。怎么办呢&#xff1f; 打开 开发者模式 在 console 里粘贴以下&#xff0c;回车&#xff0c;就好了&#xff01; // 隐藏所有可能包含侧边栏的常见元素 var style document.createElement(style); style.in…

作者头像 李华
网站建设 2026/8/27 23:50:11

深度学习之第八课迁移学习(残差网络ResNet)

目录 简介 一、迁移学习 1.什么是迁移学习 2. 迁移学习的步骤 二、残差网络ResNet 1.了解ResNet 2.ResNet网络—残差结构 三、代码分析 1. 导入必要的库 2. 模型准备(迁移学习) 3. 数据预处理 4. 自定义数据集类 5. 数据加载器 6. 设备配置 7. 训练函数 8. 测…

作者头像 李华