Linux内核模块怎么编写与加载?嵌入式Linux面试从module_init讲到insmod

浏览量:79
时间: 2026-09-03 13:53:27
嵌入式Linux中级技术基础题

嵌入式Linux终端显示insmod加载内核模块日志

本文概述

内核模块(Kernel Module)是嵌入式Linux工程师的基本功,也是面试必考题。面试官想听的不是背一个hello模块模板,而是你懂不懂模块机制的本质——它和内置驱动有什么区别、模块生命周期(加载/初始化/退出/卸载)、module_init与module_exit、编译方式(in-tree/out-of-tree)、insmod与modprobe的依赖处理,以及调试手段(dmesg、sysfs、/proc)。本文从“模块是动态加载的驱动单元”讲起,给出模块骨架与Makefile写法,再讲加载卸载、依赖、符号导出与常见坑,强调内核版本、头文件路径和编译环境决定具体写法。

核心要点

  • 内核模块是可在运行时加载/卸载的驱动单元,与编译进内核的驱动各有适用场景

  • 模块生命周期:module_init→probe/初始化→module_exit,对应加载和卸载

  • out-of-tree模块用Makefile+Kbuild编译,依赖目标内核的构建环境与头文件

  • insmod直接加载指定模块,modprobe处理模块依赖与配置(.ko路径、依赖关系)

  • dmesg、/proc、sysfs是模块调试与验证的主要途径

  • 模块并发、引用计数、符号导出(EXPORT_SYMBOL)是写模块必须处理的工程细节

你写过Linux内核模块吗?内核模块怎么编写、编译和加载?module_init、insmod、modprobe这些是什么?

面试官考察点

考察点拆解

面试官问内核模块,重点考察:第一,你懂不懂模块机制的原理,而不是只会抄hello.c;第二,你会不会写Makefile做out-of-tree编译,并理解为什么要对着目标内核的Kbuild;第三,你懂不懂加载卸载、依赖、符号导出这些机制,遇到insmod失败能不能定位;第四,你有没有在真实板子上编译和调试过,而不是只在电脑上跑。

追问方向:module_init和module_exit干什么、insmod和modprobe区别、模块和内置驱动的取舍、模块参数、符号导出、insmod报错怎么排查。

回答思路

答题主线

建议按“模块本质 → 骨架代码 → 编译 → 加载与依赖 → 调试与坑”来答,先讲清为什么用模块,再落到怎么写。

  1. 先讲模块的本质:运行时加载/卸载,和内置驱动的区别。

  2. 讲骨架:module_init/module_exit、license、作者信息。

  3. 讲编译:Makefile + Kbuild,out-of-tree。

  4. 讲加载:insmod/modprobe、依赖、dmesg、符号。

参考回答

我先讲内核模块的本质,再落到怎么写。

第一,内核模块是什么。内核模块是可以在系统运行时动态加载和卸载的驱动或功能单元。相比直接编译进内核(built-in),模块的好处是:开发调试时不用反复编译整个内核、可以按需加载省内存、便于分发。缺点是启动时若未加载需要额外管理加载过程。做驱动开发时,模块是调试驱动的主要方式

第二,模块骨架。一个基本模块包含:

  • module_init(func):指定加载时执行的初始化函数,一般在里面注册设备、分配资源、创建proc/sysfs节点;

  • module_exit(func):指定卸载时执行的清理函数,释放资源、注销设备;

  • MODULE_LICENSE:声明许可证,缺了会有taint警告;

  • 可选MODULE_AUTHOR/MODULE_DESCRIPTION等元信息。

初始化函数里可以调用注册接口(如register_chrdev、platform_driver_register)把驱动挂到内核。

第三,编译。out-of-tree模块用Makefile调用内核Kbuild:

obj-m += mymod.o
all:
	make -C $(KDIR) M=$(PWD) modules

其中KDIR要指向目标板对应内核的源码或构建目录,头文件、配置和模块版本要和运行内核匹配,否则insmod会报version magic不匹配。所以编译环境必须对准目标内核,不能随便在宿主机编一个就以为能用。

第四,加载与依赖。

  • insmod mymod.ko:直接加载指定模块文件,不处理依赖,适合单个模块;

  • modprobe mymod:按模块名加载,会处理依赖、参数和配置文件,推荐用于有依赖的场景。

模块间依赖通过符号引用体现:一个模块用了另一个模块EXPORT_SYMBOL导出的符号,加载时就要先加载被依赖的模块。加载结果用dmesg查看,卸载用rmmod。

第五,调试与坑。常见问题:

  • insmod失败:先看dmesg报错(版本不匹配、符号找不到、资源冲突、注册失败),逐一排查;

  • 引用计数:设备被占用时rmmod会失败,说明模块还在被引用;

  • 并发与锁:模块内并发访问要用锁保护,卸载时可能有竞态,需要正确同步。

调试时我会在init/exit加printk(用dev_info/pr_info)输出关键步骤,配合dmesg定位到哪一步失败。

面试官可能追问

追问 1:insmod和modprobe有什么区别?
insmod直接加载指定路径的.ko文件,不做依赖解析;modprobe按模块名从模块目录查找,会解析模块依赖、处理modules.dep、加载依赖模块,还能传参数。所以模块有依赖时优先用modprobe,单个独立模块调试用insmod也方便。
追问 2:insmod报version magic不匹配怎么办?
这说明模块的编译环境与运行内核不一致(内核版本、配置或vermagic不同)。解决:用目标内核的源码和构建目录重新编译模块,确保KDIR指向运行内核对应的源码;或确认内核配置(如CONFIG_MODVERSIONS)一致。检查uname -r和模块modinfo输出对比。
追问 3:模块和内置驱动怎么取舍?
内置驱动随内核启动、无需加载管理、适合系统必须的驱动(如根文件系统设备);模块灵活、便于开发调试和按需加载、适合可选的或外设驱动。量产时关键驱动常内置,开发期用模块。具体取舍看启动需求、大小和分发方式。
追问 4:EXPORT_SYMBOL是干什么的?
它把模块里的符号导出到内核符号表,让其他模块或内核代码能调用。模块A用了模块B导出的符号,加载时就产生依赖,需要先加载B。导出符号要谨慎,避免污染内核命名空间。
追问 5:模块并发访问怎么保护?
和内核其他代码一样,用自旋锁、互斥锁等保护共享数据;字符设备还要考虑并发打开、多线程读写。写模块时要注意竞态:init里注册、open/ioctl并发访问、exit卸载时可能有正在运行的调用,需要引用计数和同步机制配合。

常见失分表达

✗ “模块就是写个hello.c,insmod就能跑” — 忽略编译环境、版本匹配、依赖和资源管理,只停留在模板层面。
✗ “insmod和modprobe一样” — 混淆依赖处理机制,遇到依赖问题不会处理。
✗ “模块报错就重编一遍” — 不先看dmesg定位原因,盲目重编无法解决问题。
✗ “不用管license” — MODULE_LICENSE缺失会导致内核taint,不专业。
✗ “模块卸载很安全,不会有事” — 忽略引用计数与并发竞态,卸载时容易崩溃。

准备动作

1
在目标板或开发环境里实际写一个带注册和打印的内核模块,用insmod/rmmod跑通,记录dmesg输出
2
把Makefile的KDIR指向目标内核,练习out-of-tree编译,理解Kbuild机制
3
做一个故意引入版本不匹配的模块,用dmesg排查报错,形成排查套路
4
研究modprobe的依赖解析(modules.dep),理解模块间符号依赖怎么管理

核心关键词

内核模块Kernel Modulemodule_initmodule_exitinsmodmodprobeKbuilddmesg驱动开发


声明:本网站所收集的部分公开资料来源于互联网,转载的目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。仅供学习交流使用,不构成商业目的。版权归原作者所有,如果您发现网站上有侵犯您的知识产权的作品,请与我们取得联系,我们会及时删除。侵权投诉
相关推荐HOT
开班信息