尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

Linux内核驱动编译实战:从环境搭建到交叉编译与问题排查

Linux内核驱动编译实战:从环境搭建到交叉编译与问题排查
📅 发布时间:2026/7/29 13:33:38

1. 项目概述:为什么内核驱动编译是道坎?

搞Linux驱动开发,或者仅仅是需要给特定硬件打上补丁的朋友,一定都绕不开“编译内核驱动”这个环节。这听起来像是资深工程师的专属领域,但实际上,无论是为了启用一块新网卡、调试一个外设,还是单纯想了解系统底层是如何工作的,掌握内核驱动的编译方法都是一项非常实用的技能。很多人第一次尝试时,往往会被复杂的Kbuild系统、版本依赖和层出不穷的编译错误劝退,感觉像是在迷宫里打转。

其实,内核驱动的编译并没有想象中那么神秘。它本质上是一套高度自动化但又极其严谨的构建流程。说它自动化,是因为你通常不需要手动指定每一个源文件和链接库;说它严谨,是因为它对你的编译环境、内核版本、配置选项有着近乎苛刻的要求。一个驱动模块(.ko文件)能否成功加载并运行,编译这一步就决定了八成。我见过不少新手,驱动代码逻辑写得没问题,却卡在编译上,反复折腾一两天,最后发现只是少装了一个kernel-headers包,或者Makefile里少写了一个-C参数。

所以,这篇内容的目的,就是帮你把这道坎踏平。我们不谈艰深的驱动原理,就聚焦在“如何正确地编译出一个能用的内核模块”这个实操目标上。我会从最基础的环境准备讲起,带你一步步拆解内核的Kbuild构建系统,然后分别演示“为当前运行的内核编译驱动”和“为特定内核版本编译驱动”这两种最常遇到的场景,最后把那些最容易踩坑的地方和排查技巧掰开揉碎讲清楚。无论你是嵌入式开发者、运维工程师,还是对Linux底层感兴趣的学习者,这套方法都能让你在遇到驱动编译问题时,心里有底,手上有招。

2. 编译环境与内核头文件:打好地基

在动手写Makefile之前,一个正确、完整的编译环境是成功的前提。很多人编译失败,第一步就栽在这里。

2.1 确认当前内核版本与获取头文件

编译内核驱动,核心是要针对某个特定版本的内核源代码进行。最常用的场景就是为你当前正在运行的系统编译驱动。首先,你必须知道你系统的内核版本。

打开终端,运行:

uname -r

你会得到一个像5.15.0-91-generic这样的输出。这不仅仅是版本号,后面的-generic是发行版定制后缀,同样重要。

接下来,你需要获取与这个完全一致版本的内核头文件或完整源代码。头文件(位于/usr/src/linux-headers-$(uname -r)/)包含了内核数据结构、函数声明和配置参数,是编译驱动时必须的“接口说明书”。

在Ubuntu/Debian系系统上,安装头文件包非常简单:

sudo apt update sudo apt install linux-headers-$(uname -r)

这个命令会安装与你当前运行内核精确匹配的头文件包。安装完成后,你可以在/usr/src/目录下找到对应的文件夹。

注意:linux-headers包和linux-source包不同。前者只包含编译所需头文件和构建框架,体积较小;后者是完整的内核源代码。对于驱动编译,通常安装linux-headers就足够了。

在RHEL/CentOS/Fedora系系统上,命令略有不同:

sudo yum install kernel-devel-$(uname -r) # 或者使用dnf sudo dnf install kernel-devel-$(uname -r)

同样,请确保安装的版本与uname -r的输出完全一致。

2.2 安装必要的编译工具链

仅有头文件还不够,你还需要编译器、链接器等一套完整的工具链。最基本的包括:

  • gcc: GNU C编译器,编译内核和驱动的核心工具。
  • make: 构建自动化工具,用于解析和执行Makefile。
  • libc-dev: C标准库的开发文件。

在Ubuntu/Debian上,可以一次性安装:

sudo apt install build-essential

这个build-essential元包会自动安装gcc,g++,make,libc6-dev等必备工具。

在RHEL/CentOS/Fedora上,对应的组是Development Tools:

sudo yum groupinstall "Development Tools" # 或 sudo dnf groupinstall "Development Tools"

2.3 一个关键检查:内核版本与头文件是否匹配?

这是最经典的坑。有时候,系统更新了内核(uname -r显示新版本),但linux-headers包还没来得及安装,或者安装的是旧版本。这会导致编译时找不到正确的头文件路径。

检查方法:

  1. 确认已安装的头文件包版本:
    dpkg -l | grep linux-headers # Debian/Ubuntu rpm -qa | grep kernel-devel # RHEL/CentOS/Fedora
  2. 对比uname -r的输出与已安装的头文件版本号。必须完全一致,包括后缀。
  3. 检查头文件目录是否存在:
    ls -d /usr/src/linux-headers-$(uname -r)

如果不匹配,你需要手动安装正确版本的头文件,或者重启系统进入新内核后再安装。我曾遇到过服务器自动更新内核后,驱动编译失败,就是因为重启后才触发了头文件包的安装。

3. 理解内核构建系统:Kbuild是如何工作的?

在随便写一个Makefile之前,我们必须先理解Linux内核独特的构建系统——Kbuild。它不是你平时编译普通C程序用的那种简单的Makefile,而是一套复杂、精密且高度集成的规则集合。驱动编译的Makefile,实际上是“调用”了内核源码树中的这套Kbuild系统。

3.1 核心思想:向内核构建系统“注册”你的模块

当你编译一个独立的内核模块时,你不是在独立地编译一个程序。你的Makefile主要做两件事:

  1. 切换目录:通过-C选项,告诉make命令:“请先切换到内核源代码(或头文件)的目录下,读取那里顶层的Makefile。”
  2. 指明目标:通过M=选项,告诉内核的构建系统:“我的模块源代码在另外一个独立的目录(M=指定的路径)里,请你根据内核的规则来构建它。”

这样,构建的主动权交给了内核的Kbuild。Kbuild系统会:

  • 使用内核配置(.config文件)来决定哪些函数和宏是可用的。
  • 使用内核的编译标志(如优化级别、架构相关参数)。
  • 处理模块所有的依赖关系,比如你的驱动依赖了内核的USB子系统或网络协议栈。

3.2 一个最小化的驱动Makefile解析

下面是一个最经典、最精简的驱动Makefile示例,适用于只有一个源文件hello.c的模块:

# 指定内核源码目录,使用绝对路径更可靠 KDIR := /lib/modules/$(shell uname -r)/build # 或者使用头文件目录 # KDIR := /usr/src/linux-headers-$(shell uname -r) # 指定当前模块源码所在目录 PWD := $(shell pwd) # 定义模块名(最终生成的.ko文件名) obj-m += hello.o # 如果你的模块由多个源文件组成,比如file1.c, file2.c # 则需要将模块名改为一个“复合对象” # obj-m += mymodule.o # mymodule-objs := file1.o file2.o all: $(MAKE) -C $(KDIR) M=$(PWD) modules clean: $(MAKE) -C $(KDIR) M=$(PWD) clean

逐行解读:

  • KDIR: 这是最关键的一行。它指向了内核的构建目录。/lib/modules/$(shell uname -r)/build是一个标准的符号链接,通常指向已安装的内核头文件目录。使用$(shell uname -r)可以自动获取当前内核版本,使脚本更具可移植性。
  • PWD: 获取当前驱动源码所在的绝对路径。
  • obj-m += hello.o: 这是告诉Kbuild:“请将hello.o构建成一个内核模块(m代表module)。Kbuild会自动寻找hello.c来编译。如果模块名和源文件名不同(例如源文件是mydrv.c,但想生成foobar.ko),则需要写obj-m += foobar.o,并添加foobar-objs := mydrv.o。
  • all目标:执行真正的编译命令。$(MAKE)是make命令的引用。
    • -C $(KDIR): 先改变目录到内核构建目录。
    • M=$(PWD): 然后告诉内核Makefile,模块源码在$(PWD)这个外部目录。
    • modules: 这是内核Makefile中定义的一个目标,意思是“构建所有在obj-m列表中指定的模块”。
  • clean目标:清理编译生成的文件,同样需要调用内核的构建系统来执行清理工作。

3.3 多文件模块与依赖处理

当你的驱动变得复杂,需要拆分成多个.c和.h文件时,Makefile需要稍作调整。假设你有main.c,helper.c,helper.h三个文件,想生成mydrv.ko。

obj-m += mydrv.o mydrv-objs := main.o helper.o

这里,mydrv.o不是一个实际存在的源文件,而是一个“组合对象”。Kbuild看到这个规则后,会先去编译main.c生成main.o,编译helper.c生成helper.o,最后将这两个.o文件链接成mydrv.ko模块。头文件helper.h的依赖关系会被自动处理(只要你在main.c和helper.c中正确#include了它)。

实操心得:在编写多文件驱动时,务必确保头文件中使用#ifndef ... #define ... #endif防止重复包含,并且函数声明要清晰。编译时如果出现未定义的引用错误,首先检查mydrv-objs列表是否包含了所有必需的.o文件。

4. 标准编译流程实操:从代码到.ko文件

环境准备好了,Makefile也理解了,现在我们来走一遍完整的编译流程。我会用一个最简单的“Hello World”内核模块作为例子。

4.1 编写最简单的内核模块代码

创建一个名为hello.c的文件,内容如下:

#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> // 模块的初始化函数,在insmod时调用 static int __init hello_init(void) { printk(KERN_INFO "Hello, World! Kernel module loaded.\n"); return 0; // 返回0表示成功 } // 模块的清理函数,在rmmod时调用 static void __exit hello_exit(void) { printk(KERN_INFO "Goodbye, World! Kernel module unloaded.\n"); } // 注册初始化和清理函数 module_init(hello_init); module_exit(hello_exit); // 模块的元信息 MODULE_LICENSE("GPL"); // 许可证声明,必须(否则会有警告) MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("A simple hello world kernel module"); MODULE_VERSION("0.1");

这段代码做了几件事:

  1. 包含必要的内核头文件。
  2. 定义了一个初始化函数hello_init,它通过printk(内核的打印函数)向内核日志输出一条信息。
  3. 定义了一个清理函数hello_exit,在模块卸载时输出信息。
  4. 使用module_init和module_exit宏将这两个函数注册为模块的入口和出口。
  5. 添加模块信息,其中MODULE_LICENSE(“GPL”)几乎是强制性的,使用非GPL兼容的许可证可能会导致模块无法加载或内核被污染。

4.2 编写对应的Makefile

在同一目录下创建Makefile(注意M大写),内容就是上一节给出的最小化示例。

4.3 执行编译

在终端中,切换到hello.c和Makefile所在的目录,直接执行make命令:

make

如果一切顺利,你将看到类似以下的输出:

make -C /lib/modules/5.15.0-91-generic/build M=/home/your/path/to/module modules make[1]: Entering directory '/usr/src/linux-headers-5.15.0-91-generic' CC [M] /home/your/path/to/module/hello.o MODPOST /home/your/path/to/module/Module.symvers CC [M] /home/your/path/to/module/hello.mod.o LD [M] /home/your/path/to/module/hello.ko BTF [M] /home/your/path/to/module/hello.ko Skipping BTF generation for /home/your/path/to/module/hello.ko due to unavailability of vmlinux make[1]: Leaving directory '/usr/src/linux-headers-5.15.0-91-generic'

这个过程依次执行了:

  1. 编译(CC):将hello.c编译成hello.o目标文件。
  2. 模块后处理(MODPOST):处理模块间的符号依赖,生成Module.symvers文件(记录符号版本信息)。
  3. 编译模块元数据:生成hello.mod.o。
  4. 链接(LD):将hello.o和hello.mod.o链接成最终的内核模块文件hello.ko。
  5. BTF生成:尝试为模块生成BPF Type Format信息(用于更新的调试工具),这里因为缺少vmlinux文件跳过了,不影响模块功能。

编译成功后,目录下会生成一堆文件,其中最关键的就是hello.ko。

4.4 加载、测试与卸载模块

现在,你可以将这个模块插入到运行中的内核里:

sudo insmod hello.ko

这条命令没有输出是正常的。如何验证模块已经加载了呢?使用dmesg命令查看内核环形缓冲区的最新消息:

dmesg | tail -5

你应该能看到类似这样的输出:

[ 1234.567890] Hello, World! Kernel module loaded.

这说明我们的初始化函数被成功调用了。你还可以用lsmod命令查看所有已加载的模块,hello模块应该会在列表中。

卸载模块使用rmmod命令,注意参数是模块名(不是文件名):

sudo rmmod hello

再次使用dmesg | tail -5查看,应该能看到清理函数输出的告别信息。

注意事项:insmod/rmmod需要root权限。模块加载后,其代码就成为内核空间的一部分,拥有极高的权限。务必确保你加载的模块来自可信来源。编写有缺陷的模块可能导致内核崩溃(Kernel Panic)。

5. 为特定内核版本交叉编译驱动

很多时候,我们不是在为当前运行的系统编译驱动。比如,在x86的PC上为ARM架构的嵌入式开发板编译驱动,或者为某个未安装的特定内核版本(如服务器内核)编译驱动。这就需要交叉编译。

5.1 核心概念:指定KERNEL_DIR和ARCH

交叉编译的关键在于为make命令明确指定两个参数:

  • KERNEL_DIR:目标内核的完整源代码目录的路径。注意,这里不再是头文件目录(/lib/modules/.../build),而必须是配置并准备用于构建的完整源码树。
  • ARCH:目标CPU的架构,如arm,arm64,x86_64等。
  • CROSS_COMPILE:交叉编译工具链的前缀。

5.2 准备目标内核源码树

你不能使用发行版提供的linux-headers包进行交叉编译,因为它不包含完整的源码和构建框架。你必须获取目标内核的完整源代码,并进行最小化配置。

假设你的目标板是ARM架构,内核版本是5.10.123。

  1. 下载或复制目标内核源码到你的开发主机,例如路径为/home/you/linux-5.10.123/。
  2. 进入该目录,进行配置。最快捷的方式是使用目标板配置文件(如果存在):
    cd /home/you/linux-5.10.123 # 假设你的板子供应商提供了defconfig文件 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- your_board_defconfig # 如果没有特定defconfig,可以使用一个通用的最小配置 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- defconfig
    这个步骤会生成内核构建所需的.config文件。

5.3 修改驱动Makefile以支持交叉编译

你的驱动Makefile需要做相应调整,不再使用$(shell uname -r)来自动获取路径,而是硬编码或通过外部变量传入。

# 方法1:在Makefile中硬编码(不灵活) # KERNEL_DIR ?= /home/you/linux-5.10.123 # ARCH ?= arm # CROSS_COMPILE ?= arm-linux-gnueabihf- # 方法2:通过命令行参数传入(推荐) KERNEL_DIR ?= /lib/modules/$(shell uname -r)/build ARCH ?= x86_64 CROSS_COMPILE ?= PWD := $(shell pwd) obj-m += hello.o all: $(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_DIR) M=$(PWD) modules clean: $(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNEL_DIR) M=$(PWD) clean

5.4 执行交叉编译命令

在驱动源码目录下,通过命令行覆盖Makefile中的变量:

make KERNEL_DIR=/home/you/linux-5.10.123 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

这条命令做了以下事情:

  • 调用/home/you/linux-5.10.123/目录下的内核顶层Makefile。
  • 告知内核构建系统,目标架构是arm。
  • 使用arm-linux-gnueabihf-gcc等工具进行编译(CROSS_COMPILE指定了工具链前缀)。
  • 最终在驱动源码目录生成hello.ko,但这个.ko文件是ARM架构的,无法在x86主机上加载运行。

实操心得:交叉编译时最常见的错误是“找不到文件”或“架构不匹配”。务必确保:

  1. KERNEL_DIR指向的内核源码树已经执行过make ... _defconfig,生成了有效的.config文件。
  2. CROSS_COMPILE指定的工具链前缀路径已加入PATH环境变量,并且版本与内核兼容。可以通过arm-linux-gnueabihf-gcc --version来测试。
  3. 驱动源码中不要包含任何与主机架构相关的硬编码路径或假设。

6. 编译问题深度排查与解决技巧

即使按照步骤操作,编译过程也常常不会一帆风顺。下面是一些常见错误及其排查思路,这些是我在多年实践中积累下来的“血泪经验”。

6.1 错误:Makefile:xxx: *** No rule to make target ‘modules’。 Stop。

问题分析:这几乎总是因为KDIR或KERNEL_DIR路径设置错误。make -C $(KDIR)命令首先会尝试进入该目录并寻找Makefile。如果路径不对,或者该目录下没有内核的顶层Makefile,就会报这个错。

排查步骤:

  1. 检查路径是否存在:ls -ld $(KDIR)。确保它指向一个真实目录。
  2. 检查路径内容:ls $(KDIR)/Makefile。确认该目录下存在内核的Makefile文件。
  3. 确认路径含义:
    • 如果为当前主机编译,KDIR通常是/lib/modules/$(uname -r)/build,这是一个符号链接。用ls -l /lib/modules/$(uname -r)/build查看它指向哪里,通常是/usr/src/linux-headers-$(uname -r)。确保这个目标目录存在且完整。
    • 如果交叉编译,KERNEL_DIR必须是完整的内核源码目录,并且已经配置过(存在.config文件)。

6.2 错误:fatal error: linux/module.h: No such file or directory

问题分析:编译器找不到内核头文件。这通常意味着:

  1. 没有安装对应版本的linux-headers或kernel-devel包。
  2. KDIR指向了错误的位置(例如指向了普通源码目录而非包含构建框架的头文件目录)。
  3. 在交叉编译时,ARCH或CROSS_COMPILE设置错误,导致构建系统去错误的位置寻找头文件。

排查步骤:

  1. 为主机编译:运行apt list --installed | grep linux-headers或rpm -qa | grep kernel-devel,确认包已安装且版本匹配。
  2. 检查KDIR目录下的include/linux/module.h文件是否存在。
  3. 交叉编译:确保已在内核源码目录执行过make ARCH=xxx ... menuconfig或... defconfig,这会生成必要的头文件链接和配置。

6.3 错误:error: expected ‘;’ before ‘XXXX’或类型未定义

问题分析:这属于代码语法或语义错误,但有时根源在环境。

  1. 内核API变更:这是驱动开发者最头疼的问题。不同内核版本间,函数原型、数据结构、头文件位置可能会发生变化。你在网上找到的驱动代码,可能是针对旧内核写的。例如,create_proc_entry()函数在较新内核中被proc_create()取代了。
  2. 配置依赖:你的驱动代码使用了某个内核配置选项(如CONFIG_PCI)下的函数或宏,但当前内核的.config文件没有启用该选项。

排查步骤:

  1. 核对内核版本:确认你的驱动代码设计针对的内核版本,与你正在编译的内核版本是否兼容。查看内核的ChangeLog或Documentation/目录下的更新说明。
  2. 使用内核的make命令检查配置:在内核源码目录下,运行make ARCH=$(ARCH) menuconfig,搜索错误信息中提到的函数或配置选项,看它是否被启用([*]或[M])。
  3. 查阅当前内核头文件:直接去$(KDIR)/include/或$(KDIR)/include/linux/下查看相关头文件,确认函数原型、宏定义是否存在、是否改变。
  4. 为驱动代码添加版本兼容宏:这是专业驱动开发中的常用技巧。
    #include <linux/version.h> #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) // 使用新内核的API my_struct = new_api_call(); #else // 使用旧内核的API my_struct = old_api_call(); #endif

6.4 错误:MODPOST阶段报错,如未定义的符号

问题分析:MODPOST阶段负责解决模块间的符号引用。如果出现undefined symbol: _function_name,意味着:

  • 你的模块调用了一个内核函数,但该函数没有被导出(即不在内核的符号表中)。
  • 或者,你依赖另一个内核模块(如cfg80211.ko)导出的符号,但编译时没有解决这个依赖。

排查步骤:

  1. 检查函数是否导出:在内核源码目录,查看/proc/kallsyms(需要root)或编译生成的Module.symvers文件,搜索你调用的函数名。如果找不到,说明该函数是内核内部函数,模块不能直接调用。
  2. 使用EXPORT_SYMBOL():只有被EXPORT_SYMBOL()或EXPORT_SYMBOL_GPL()显式导出的函数,才能被模块使用。你只能调用这些已导出的函数。
  3. 处理模块依赖:如果你的模块A.ko依赖于模块B.ko导出的符号,你需要在A.ko的Makefile中添加:
    KBUILD_EXTRA_SYMBOLS += /path/to/B/Module.symvers
    并且在加载时,需要先insmod B.ko,再insmod A.ko。更规范的做法是将B编译进内核(=y),而不是编成模块(=m)。

6.5 调试与信息输出技巧

编译成功只是第一步,加载运行可能还有问题。除了用printk,还有更多调试手段:

  1. 使用pr_debug,dev_dbg等动态调试:这些宏在默认配置下不会打印信息,但可以通过dynamic debug机制在运行时开启,避免日志被刷屏。

    // 在代码中 pr_debug(“Debug info: value=%d\n”, var); // 在shell中,动态开启该文件的调试信息 echo ‘file hello.c +p’ > /sys/kernel/debug/dynamic_debug/control
  2. 查看详细的编译命令:在make命令前加上V=1,可以打印出Kbuild实际执行的每一条编译和链接命令,对于排查工具链、参数问题非常有用。

    make V=1
  3. 清理与重新构建:当修改了Makefile或头文件依赖关系时,简单的make可能不够彻底。使用make clean清理旧文件,或者更彻底地删除所有生成文件(包括Module.symvers,.mod.c等)再重新编译。

7. 进阶话题:集成到内核树与DKMS

当你只是测试一个简单的模块,放在独立目录编译没问题。但如果驱动比较复杂,或者希望长期维护、分发给他人使用,就需要更规范的方法。

7.1 将驱动集成到内核源码树

这是最正规的驱动开发方式。你将驱动的源代码(如mydriver/目录)放置在内核源码的drivers/某个子目录下(例如drivers/char/或drivers/net/wireless/)。

  1. 创建驱动目录和Kconfig/Makefile:

    • 在drivers/char/mydriver/目录下,放置你的.c和.h文件。
    • 创建Kconfig文件,定义在make menuconfig时出现的配置选项。
      config MY_DRIVER tristate “My Hardware Driver” depends on PCI help This is a sample driver for my special hardware.
    • 创建Makefile,内容非常简单:
      obj-$(CONFIG_MY_DRIVER) += mydriver.o mydriver-objs := main.o helper.o
  2. 修改上级目录的Kconfig和Makefile:

    • 在drivers/char/Kconfig末尾添加:source “drivers/char/mydriver/Kconfig”
    • 在drivers/char/Makefile末尾添加:obj-$(CONFIG_MY_DRIVER) += mydriver/
  3. 配置与编译:

    • 回到内核根目录,执行make menuconfig,在Device Drivers -> Character devices下就能看到你的驱动选项,可以将其编入内核(*)或编成模块(M)。
    • 执行make或make modules,你的驱动就会随内核一起被编译。模块会生成在drivers/char/mydriver/目录下,或者统一输出到modules目录。

这种方式的好处是驱动可以享受内核完整的版本控制和配置系统,但缺点是必须拥有并管理整个内核源码树。

7.2 使用DKMS管理第三方内核模块

DKMS(Dynamic Kernel Module Support)是发行版(如Ubuntu, RHEL)用来管理第三方内核模块(如NVIDIA显卡驱动、VirtualBox增强工具)的标准框架。它的核心作用是:在内核升级后,自动为新的内核重新编译模块。

假设你有一个驱动hello-1.0,想用DKMS管理。

  1. 创建DKMS源码目录结构:

    /usr/src/hello-1.0/ ├── Makefile ├── hello.c └── dkms.conf
  2. 编写dkms.conf配置文件:

    PACKAGE_NAME=“hello” PACKAGE_VERSION=“1.0” MAKE[0]=“make all KVERSION=$kernelver” CLEAN=“make clean” BUILT_MODULE_NAME[0]=“hello” DEST_MODULE_LOCATION[0]=“/extra” AUTOINSTALL=“yes”
    • PACKAGE_NAME,PACKAGE_VERSION: 模块包名和版本。
    • MAKE: 编译命令,$kernelver是DKMS传递的内核版本变量。
    • BUILT_MODULE_NAME: 生成的模块名(不含.ko)。
    • DEST_MODULE_LOCATION: 安装路径,相对于/lib/modules/$kernelver/。/extra是一个常用目录。
  3. DKMS操作命令:

    • 添加模块到DKMS树:sudo dkms add -m hello -v 1.0
    • 为当前内核编译模块:sudo dkms build -m hello -v 1.0 -k $(uname -r)
    • 安装模块:sudo dkms install -m hello -v 1.0 -k $(uname -r)
    • 检查状态:dkms status
    • 移除模块:sudo dkms remove -m hello -v 1.0 --all

安装后,模块文件(如hello.ko)会被安装到/lib/modules/$(uname -r)/extra/目录。当系统通过包管理器升级内核后,DKMS服务会检测到,并自动为新内核重新编译和安装这个模块,这极大地简化了第三方驱动的维护。

注意事项:DKMS要求你的Makefile能够接受KVERSION参数来指定内核版本。我们之前写的通用Makefile通过$(shell uname -r)获取版本,这在DKMS自动构建时可能不工作。一个更兼容的写法是:

KVER ?= $(shell uname -r) KDIR ?= /lib/modules/$(KVER)/build ... all: $(MAKE) -C $(KDIR) M=$(PWD) modules

这样,DKMS调用make all KVERSION=5.15.0-92-generic时,就会覆盖KVER变量,从而为指定内核编译。

相关新闻

  • 株洲千帆用户评价如何 - 工业品网
  • PS 贴图怎么贴到头巾上?4 种原生工具完整零基础实操教程
  • 试听转化:教育出海如何接住最后一公里

最新新闻

  • 理工科申博的实验室选择:如何评估课题组实力、识别“坑导“
  • 如何快速掌握免费RPA工具:3小时从零到自动化实战指南
  • 2026年户外大型铜雕厂家挑选攻略及行业参考 - 曲阳嘉华园林
  • 2026宝山区冷链干冰厂家哪家好,干冰清洗机厂家推荐:采购避坑指南与5个选购要点 - geo88
  • 上海五一活动精选:从信息筛选到体验优化的全链路指南
  • 三参数陷波滤波器:从s域到z域的MATLAB实现与工程实践

日新闻

  • 金融舆情监测系统:多语言情感分析与实时可视化技术解析
  • QT C++调用Python异常处理:PyBind11实战与跨语言编程指南
  • A-47双麦回音消除模块:主次麦空间分布与差分连接对ENC性能的影响

周新闻

  • 大连理工大学与东京大学联手打造的“主动型AI助手“
  • 170.2026年国家级科研瓶颈:超精密单点金刚石切削(SPDT)光学表面生成
  • SongBloom:革命性歌曲生成框架深度解析——如何通过交织自回归与扩散模型创作完整音乐

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号