Linux 内核模块开发简介
设备类型分类
类型
缩写
访问方式
典型设备
特殊文件
块设备
blkdevs
按块随机访问(支持寻址)
硬盘/SSD/光驱
/dev/sda1
字符设备
cdevs
字节流顺序访问
键盘/打印机/伪设备
/dev/ttyS0
网络设备
-
套接字API(破坏"一切皆文件"原则)
网卡/无线适配器
无设备节点
混杂设备
miscdevs
字符设备简化形式
简单专用设备
/dev/random
等
伪设备示例 : 1 2 3 4 5 /dev/random /dev/null /dev/zero /dev/full /dev/mem
内核模块开发
Hello World 模块示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> static int __init hello_init (void ) { printk(KERN_INFO "I bear a charmed life.\n" ); return 0 ; } static void __exit hello_exit (void ) { printk(KERN_INFO "Out, out, brief candle!\n" ); } module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE("GPL" ); MODULE_AUTHOR("Shakespeare" ); MODULE_DESCRIPTION("Hello World Module" );
关键机制解析
入口函数 :
形式:int init_func(void)
职责:注册资源/初始化硬件/分配数据结构
返回值:0=成功,非0=失败
出口函数 :
形式:void exit_func(void)
职责:释放资源/复位硬件/清理状态
许可证声明 :
非GPL模块会导致内核标记为"tainted"
无法调用GPL-only符号
模块构建指南
集成到内核源码树(推荐)
选择路径 :
字符设备 → drivers/char/
块设备 → drivers/block/
USB设备 → drivers/usb/
修改Makefile : 1 2 3 4 5 6 7 obj-$(CONFIG_FISHING_POLE) += fishing/ obj-$(CONFIG_FISHING_POLE) += fishing.o fishing-objs := main.o line.o EXTRA_CFLAGS += -DTITANIUM_POLE
外部独立构建
Makefile示例 : 1 2 3 4 5 obj-m := fishing.o fishing-objs := fishing-main.o fishing-line.o all: make -C /path/to/kernel/source M=$(PWD) modules
构建命令 : 1 2 make -C /lib/modules/$(uname -r)/build M=$PWD modules
### 模块管理
模块安装路径规范
1 2 3 4 5 /lib/modules/$(uname -r)/kernel/<源码树路径>/<模块名>.ko /lib/modules/2.6.34/kernel/drivers/char/fishing.ko
安装命令 : 1 sudo make modules_install
模块依赖管理
1. 依赖关系生成
1 2 3 4 5 sudo depmod sudo depmod -A
2. 依赖存储位置
1 2 3 4 5 /lib/modules/$(uname -r)/modules.dep kernel/drivers/char/fishing.ko: kernel/drivers/net/bait.ko
智能加载原理 :
当加载 chum.ko
时,系统自动解析其依赖并先加载
bait.ko
模块加载与卸载
基础工具(不推荐)
操作
命令
缺陷
示例
加载
insmod module.ko
无依赖解析
insmod fishing.ko
卸载
rmmod module_name
不检查依赖
rmmod fishing
高级工具(推荐)
1. 智能加载 : 1 2 3 4 5 sudo modprobe fishing pole_length=300 material=titanium lsmod | grep fishing
2. 安全卸载 : 1 2 3 4 5 sudo modprobe -r fishing sudo modprobe -rf fishing
内核配置与模块开发高级指南
配置选项管理 (Kconfig)
linux使用kbuild系统,可以通过修改Kconfig文件便捷地管理配置选项
1 2 3 4 5 6 7 8 9 # drivers/char/Kconfig 示例 config FISHING_POLE tristate "Fish Master 3000 support" # 三态选项,表示模块在编译配置中可以内置编译到内核(Y),作为模块编译(M)或者不编译(N) default n # 默认禁用 depends on FISH_TANK && !NO_FISHING # 依赖条件 select BAIT # 强制关联选项 help # 帮助文档 Enable support for the Fish Master 3000 computer interface. Choose Y to build into kernel, M for module (fishing.ko), or N to disable.
核心指令解析 : | 指令 |
功能 | 示例 |
|----------------|----------------------------------|-----------------------------------|
| tristate
| 三态选项 (Y/M/N) | 驱动标准配置 | |
bool
| 布尔选项 (Y/N) | 特性开关 | | default
|
默认值 | default y
默认启用 | | depends on
|
依赖关系 | depends on NET
需网络支持 | |
select
| 强制启用其他选项 | select CRC32
自动启用CRC校验 | | if
| 条件显示 |
if EMBEDDED
嵌入式场景可见 | | help
| 帮助文档
| 用户配置时的说明文本 |
模块参数系统
1. 基础参数声明
1 2 3 static int pole_length = 200 ; module_param(pole_length, int , 0644 ); MODULE_PARM_DESC(pole_length, "Pole length in cm" );
2. 高级参数类型
类型
说明
示例
charp
字符串指针
module_param(name, charp, 0);
bool
布尔值
module_param(enable, bool, 0);
module_param_string
直接复制到数组
char target[32]; module_param_string(dest, target, sizeof(target), 0);
module_param_array
数组参数
int ids[5]; int count; module_param_array(ids, int, &count, 0);
3. 参数传递方式
1 2 3 4 5 6 7 sudo modprobe fishing pole_length=300 material=carbon modinfo fishing parm: pole_length:Pole length in cm (int) parm: material:Construction material (charp)
4. sysfs集成
1 2 /sys/module/fishing/parameters/pole_length
符号导出机制
1. 基础导出
1 2 3 4 5 int get_pole_strength (struct pole *p) { return p->load_capacity; } EXPORT_SYMBOL(get_pole_strength);
2. GPL受限导出
1 EXPORT_SYMBOL_GPL(calculate_bait_ratio);
3. 导出规则
导出类型
调用权限
典型场景
EXPORT_SYMBOL
所有模块
通用内核API
EXPORT_SYMBOL_GPL
仅GPL许可证模块
核心子系统接口
未导出符号
仅内核内部使用
静态函数/私有实现
配置系统元选项
1 2 3 4 5 6 7 config EXPERIMENTAL bool "Enable experimental features" # 高风险功能开关 default n config DEBUG_KERNEL bool "Kernel debugging" # 调试选项总开关 default y if DEBUG
关键元选项 : -
CONFIG_EMBEDDED
:嵌入式系统优化选项 -
CONFIG_BROKEN_ON_SMP
:标记非SMP安全驱动 -
CONFIG_EXPERIMENTAL
:实验性功能入口
开发工作流示例
1. 添加新驱动
1 2 3 4 5 6 7 8 9 10 11 config FISHING_PRO tristate "Professional Fishing Module" select FISHING_ADVANCED help Support for professional-grade fishing equipment source "drivers/char/fishing/Kconfig"
2. 实现参数化模块
1 2 3 4 5 6 7 8 9 10 11 #include <linux/moduleparam.h> static char material[20 ] = "fiberglass" ;module_param_string(material, material, sizeof (material), 0644 ); static int lengths[] = {180 , 240 };static int nr_lengths = 2 ;module_param_array(lengths, int , &nr_lengths, 0444 ); static bool enable_ai;module_param(enable_ai, bool , 0644 );
3. 编译验证
1 2 3 4 5 6 7 make menuconfig make -j$(nproc ) modules sudo make modules_install
生产环境最佳实践
参数安全 : 1 2 3 4 5 6 7 8 9 10 static int max_load = 100 ;module_param(max_load, int , 0644 ); static int __init init_func (void ) { if (max_load > MAX_SAFE_LIMIT) { pr_warn("Dangerous load limit %d, capping at %d\n" , max_load, MAX_SAFE_LIMIT); max_load = MAX_SAFE_LIMIT; } }
版本兼容 : 1 2 3 4 5 6 7 #include <linux/version.h> #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) #else #endif
错误处理 : 1 2 3 4 5 6 int err = register_device();if (err) { unregister_previous(); return err; }
性能提示 :高频访问的模块参数应复制到局部变量,避免频繁查sysfs