Linux内核模块怎么开发与调试?嵌入式Linux面试

浏览量:177
时间: 2026-09-14 19:50:06
一句话答案

内核模块是运行在内核态的可加载代码,开发用 module_init/module_exit 注册入口、MODULE_LICENSE 声明许可,通过 insmod/rmmod 加载卸载,调试靠 printk+dmesg,复杂问题用 gdb/kgdb;和用户态程序最大区别是不能直接调 libc、要小心并发和内存。

本文概述

内核模块是嵌入式 Linux 驱动开发的基础,面试常考:模块和应用的区别、入口出口、许可证声明、printk 调试、并发控制、常见崩溃原因。回答要写出一个最小模块骨架,并说清用户态和内核态的差异。

核心要点

  • 模块用 module_init()/module_exit() 注册加载卸载函数,MODULE_LICENSE("GPL") 声明许可

  • 内核态不能直接用 libc(printf/malloc),用 printk/kmalloc/vmalloc,头文件

  • 加载:insmod xxx.ko;卸载:rmmod xxx;查看:lsmod;日志:dmesg

  • 并发:多 CPU/多进程同时访问,要用 mutex/spinlock 保护共享资源

  • 常见崩溃:空指针、超时调度、内存越界,会 oops 或 panic,dmesg 看栈回溯

终端屏幕显示 make 编译内核模块、insmod/dmesg 调试输出,旁边开发板

面试官:写一个最简单的内核模块,加载打印 hello,卸载打印 bye,怎么写?怎么调试?

面试官考察点

这是手到擒来的题,写不出骨架基本没过。面试官看:①入口出口宏;②MODULE_LICENSE;③printk 级别;④怎么看 dmesg;⑤是否知道内核模块不能用 printf。

回答思路

先给最小代码骨架,再讲编译(Makefile 调内核构建系统),再讲加载调试流程,最后点出用户态和内核态的坑。

参考回答

最小模块代码。

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>

static int __init hello_init(void) {
  printk(KERN_INFO "hello from module\n");
  return 0;
}
static void __exit hello_exit(void) {
  printk(KERN_INFO "bye from module\n");
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("hello demo");

几个关键点:

  • 必须 include <linux/module.h>、<linux/init.h>,不要 include stdio.h;

  • printk 不是 printf,第一个参数是日志级别(KERN_INFO/KERN_ERR),输出到内核 ring buffer,用 dmesg 看;

  • MODULE_LICENSE("GPL") 必须有,否则会有 tainted 警告,且无法使用 GPL 导出符号;

  • 加载函数返回 0 成功,返回负值(-ENOMEM 等)表示失败。

编译。模块不直接 gcc 编译,要调内核构建系统:

obj-m := hello.o
KDIR := /lib/modules/$(shell uname -r)/build
all:
  make -C $(KDIR) M=$(PWD) modules
clean:
  make -C $(KDIR) M=$(PWD) clean

嵌入式交叉编译时 KDIR 指向目标板内核源码树,ARCH/CROSS_COMPILE 指定。

加载与调试。

操作命令说明
加载insmod hello.ko手动加载,加载失败 dmesg 看错误
查看lsmod / lsmod | grep hello看已加载模块
卸载rmmod hello引用计数为 0 才能卸载
看日志dmesg | tailprintk 输出和 oops 栈
开机自动加载/etc/modules 或 modprobe.d生产环境用 modprobe

内核态的坑。

  • 不能用浮点(多数架构)、不能访问用户空间指针(要 copy_from_user);

  • 并发:模块可能被多 CPU 同时调用,共享资源用 mutex/spinlock 保护;

  • 崩溃代价大:内核 panic 直接死机,不能像用户态程序 segfault 了重启;

  • 复杂调试用 gdb 连 kgdb、或 crash 分析 vmcore。

面试官可能追问

追问 1:模块和应用程序的本质区别?
应用在用户态,崩溃只影响自己;模块在内核态,有最高权限,崩溃整个系统挂。模块没有 libc、有自己的内存分配、要处理并发和硬件。
追问 2:printk 的日志级别怎么用?
KERN_EMERG/ALERT/CRIT/ERR/WARNING/NOTICE/INFO/DEBUG,数字越小越紧急。dmesg 或 /proc/sys/kernel/printk 控制输出级别,生产环境别打 KERN_DEBUG。
追问 3:模块依赖另一个模块怎么办?
用 modprobe 自动按依赖加载,insmod 不处理依赖。模块导出符号用 EXPORT_SYMBOL(),其他模块用 EXPORT_SYMBOL_GPL()。
追问 4:写驱动时怎么访问用户空间的数据?
不能直接解引用用户指针,要用 copy_from_user()/copy_to_user(),否则可能崩溃或安全漏洞。
追问 5:oops 是什么?
内核态异常(空指针、非法指令等)时打印的错误信息,包含栈回溯、寄存器、PID,用来定位崩溃点。oops 后系统可能继续也可能 panic。

常见失分表达

✗ 内核模块就是普通 C 程序,用 gcc 编译 — 不知道要走内核构建系统,也不能用 libc。
✗ printf 打印调试 — 内核态没有 printf,要用 printk 并 dmesg 看。
✗ MODULE_LICENSE 随便写 — 不写 GPL 会 tainted,很多符号不能用,也影响调试。

准备动作

1
在本机写一个 hello 模块,insmod/rmmod/dmesg 跑通
2
写一个带参数(module_param)的模块,学习传参
3
准备一个 oops 栈回溯例子,能看懂 PC/栈地址
4
了解字符驱动框架(file_operations、cdev_init)的基本结构

核心关键词

内核模块module_initprintkinsmodMODULE_LICENSEoops


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

一句话答案PI 仿真目标是让 PDN(电源分配网络)阻抗在所有频率下低于目标阻抗,方法是选合适容值的去耦电容组合,配合电源平面/地平面的平板电容,使阻抗曲线在目... 详情>>

2026-09-14 19:50:58
HTTPS、SSH 公钥、秘钥、对称加密、非对称加密、hash算法 ​

非对称加密: DES 秘钥⻓度为56bit AES 秘钥⻓度⽀持128 196 256非对称加密: DSA ⽤于数字签名和认证中。和RSA不同之处在于它不能⽤作... 详情>>

2024-01-30 16:40:03
射频匹配网络怎么设计?射频工程师面试从Smith圆图讲到阻抗变换

射频电路中级技术基础题本文概述阻抗匹配是射频电路设计的核心,目标是让源阻抗和负载阻抗共轭匹配,实现最大功率传输。面试中需要讲清匹配的前提(工作频率、目标阻抗、带... 详情>>

2026-08-30 10:54:10
FPGA时序不收敛怎么办?面试如何分析Setup和Hold违例

题库分类:FPGA设计 适合经验:0-3年、3-5年 难度:中高级 题型:场景排查题 面试题项目实现... 详情>>

2026-07-29 13:55:33
EFT群脉冲测试不过怎么整改?EMC工程师面试从耦合路径讲到滤波接地

EMC电磁兼容 · 中高级 · 场景排查适合岗位:EMC工程师、硬件工程师 | 经验:1-5年 | 难度:中高级本文概述EFT(电快速瞬变脉冲群)测试依据IEC... 详情>>

2026-08-29 12:19:02
开班信息