一、顶级架构一句话总结
模块参数 → 符号导出 → 版本控制 → 模块依赖 → 运行时管理
内核模块的高级特性让驱动开发更加灵活、强大和可维护。
二、模块参数(Module Parameters)
基本参数类型
#include <linux/module.h>
#include <linux/moduleparam.h>
// 整数参数
static int int_param = 100;
module_param(int_param, int, S_IRUGO);
MODULE_PARM_DESC(int_param, "An integer parameter");
// 布尔参数
static bool bool_param = false;
module_param(bool_param, bool, S_IRUGO);
MODULE_PARM_DESC(bool_param, "A boolean parameter");
// 字符数组参数
static char *str_param = "default";
module_param(str_param, charp, S_IRUGO);
MODULE_PARM_DESC(str_param, "A string parameter");
// 数组参数
static int array_param[4] = {1, 2, 3, 4};
static int array_nr = 4;
module_param_array(array_param, int, &array_nr, S_IRUGO);
MODULE_PARM_DESC(array_param, "An integer array parameter");
高级参数类型
// 使用回调函数处理参数
#include <linux/kernel.h>
struct custom_data {
int width;
int height;
};
static struct custom_data config = {640, 480};
static int param_set_config(const char *val, const struct kernel_param *kp)
{
int w, h;
if (sscanf(val, "%dx%d", &w, &h) != 2)
return -EINVAL;
config.width = w;
config.height = h;
return 0;
}
static int param_get_config(char *buffer, const struct kernel_param *kp)
{
return sprintf(buffer, "%dx%d", config.width, config.height);
}
static const struct kernel_param_ops param_ops_config = {
.set = param_set_config,
.get = param_get_config,
};
module_param_cb(config, ¶m_ops_config, &config, S_IRUGO);
MODULE_PARM_DESC(config, "Custom parameter format: widthxheight");
参数权限
| 权限值 | 含义 |
|---|---|
| S_IRUGO | 所有用户可读 |
| S_IWUSR | root可写 |
| S_IRUGO | S_IWUSR |
| 0 | 不在sysfs中显示 |
模块参数使用
# 加载时指定参数
insmod mymodule.ko int_param=200 bool_param=1 str_param=hello
# 使用数组参数
insmod mymodule.ko array_param=10,20,30,40
# 使用自定义参数
insmod mymodule.ko config=1920x1080
# 查看已加载模块的参数
ls -l /sys/module/mymodule/parameters/
cat /sys/module/mymodule/parameters/int_param
# 运行时修改参数(需要写权限)
echo 300 > /sys/module/mymodule/parameters/int_param
三、符号导出(Symbol Export)
基本符号导出
#include <linux/module.h>
// 导出全局变量
int global_var = 100;
EXPORT_SYMBOL(global_var);
// 导出函数
int add_numbers(int a, int b)
{
return a + b;
}
EXPORT_SYMBOL(add_numbers);
// GPL-only导出(只能被GPL模块使用)
void gpl_only_function(void)
{
pr_info("This is GPL-only\n");
}
EXPORT_SYMBOL_GPL(gpl_only_function);
符号使用示例
// 模块B使用模块A的符号
#include <linux/module.h>
extern int global_var;
extern int add_numbers(int a, int b);
extern void gpl_only_function(void);
static int __init module_b_init(void)
{
pr_info("global_var = %d\n", global_var);
pr_info("1 + 2 = %d\n", add_numbers(1, 2));
gpl_only_function();
return 0;
}
module_init(module_b_init);
MODULE_LICENSE("GPL"); // 必须是GPL才能使用GPL-only符号
查看导出的符号
# 查看模块导出的符号
cat /proc/kallsyms | grep mymodule
# 查看模块依赖
modinfo mymodule.ko
# 查看已加载模块的符号
ls -l /sys/module/mymodule/holders/
四、模块版本控制(Version Control)
模块版本标记
// 简单版本号
#define DRIVER_VERSION "1.0.0"
MODULE_VERSION(DRIVER_VERSION);
// 使用语义化版本
#define VERSION_MAJOR 1
#define VERSION_MINOR 0
#define VERSION_PATCH 0
#define DRIVER_VERSION KERNEL_VERSION(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH)
MODULE_VERSION("1.0.0");
// 版本信息字符串
static const char *driver_version = "1.0.0 (2024-01-01)";
module_param_string(version, driver_version, 64, S_IRUGO);
内核版本检查
#include <linux/version.h>
// 检查内核版本
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
pr_info("Running on Linux 5.10 or newer\n");
#else
pr_info("Running on Linux older than 5.10\n");
#endif
// 使用条件编译处理API变化
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
// 4.15+ 版本的API
timer_setup(&timer, timer_callback, 0);
#else
// 旧版本的API
setup_timer(&timer, timer_callback, (unsigned long)data);
#endif
Module.symvers版本信息
// 模块版本魔法
MODULE_INFO(intree, "Y"); // 标记为内核树内模块
MODULE_INFO(supported, "external"); // 标记为外部模块
五、模块依赖(Module Dependencies)
显式依赖声明
// 声明软依赖(建议加载顺序)
MODULE_SOFTDEP("pre: module_a post: module_b");
// 声明固件依赖
MODULE_FIRMWARE("my_device_fw.bin");
// 使用request_module()动态加载依赖
#include <linux/kmod.h>
static int load_dependency(void)
{
int ret;
// 同步加载依赖模块
ret = request_module("module_a");
if (ret < 0) {
pr_err("Failed to load module_a: %d\n", ret);
return ret;
}
return 0;
}
// 异步加载依赖模块
static void async_load_dependency(struct work_struct *work)
{
request_module("module_b");
}
static DECLARE_WORK(async_load_work, async_load_dependency);
schedule_work(&async_load_work);
模块间通信
// 模块A:提供接口
#include <linux/notifier.h>
static BLOCKING_NOTIFIER_HEAD(my_notifier_list);
int register_my_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&my_notifier_list, nb);
}
EXPORT_SYMBOL(register_my_notifier);
int unregister_my_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_unregister(&my_notifier_list, nb);
}
EXPORT_SYMBOL(unregister_my_notifier);
void notify_event(int event, void *data)
{
blocking_notifier_call_chain(&my_notifier_list, event, data);
}
// 模块B:注册监听
#include <linux/notifier.h>
extern int register_my_notifier(struct notifier_block *nb);
extern int unregister_my_notifier(struct notifier_block *nb);
static int my_notifier_callback(struct notifier_block *nb,
unsigned long action, void *data)
{
pr_info("Received event: %lu, data: %p\n", action, data);
return NOTIFY_OK;
}
static struct notifier_block my_notifier = {
.notifier_call = my_notifier_callback,
};
static int __init module_b_init(void)
{
return register_my_notifier(&my_notifier);
}
static void __exit module_b_exit(void)
{
unregister_my_notifier(&my_notifier);
}
module_init(module_b_init);
module_exit(module_b_exit);
MODULE_LICENSE("GPL");
六、模块运行时管理
模块引用计数
#include <linux/module.h>
#include <linux/fs.h>
static int my_open(struct inode *inode, struct file *file)
{
// 增加模块引用计数
if (!try_module_get(THIS_MODULE))
return -EBUSY;
return 0;
}
static int my_release(struct inode *inode, struct file *file)
{
// 减少模块引用计数
module_put(THIS_MODULE);
return 0;
}
static const struct file_operations my_fops = {
.owner = THIS_MODULE, // 自动管理引用计数
.open = my_open,
.release = my_release,
};
模块状态查询
#include <linux/module.h>
// 查询模块信息
static void print_module_info(void)
{
struct module *mod;
// 遍历所有模块
list_for_each_entry(mod, THIS_MODULE->list, list) {
pr_info("Module: %s, refcnt: %d\n",
mod->name, module_refcount(mod));
}
}
// 检查模块是否加载
bool is_module_loaded(const char *name)
{
struct module *mod;
bool found = false;
preempt_disable();
mod = find_module(name);
if (mod)
found = true;
preempt_enable();
return found;
}
七、高级模块特性
1. 模块别名(Module Aliases)
// 设备表和模块别名
#include <linux/mod_devicetable.h>
static const struct of_device_id my_of_match[] = {
{ .compatible = "vendor,mydevice", },
{ }
};
MODULE_DEVICE_TABLE(of, my_of_match);
static const struct pci_device_id my_pci_match[] = {
{ PCI_DEVICE(0x1234, 0x5678) },
{ }
};
MODULE_DEVICE_TABLE(pci, my_pci_match);
// 自定义模块别名
MODULE_ALIAS("my-module-alias");
2. 模块初始化优先级
// 不同优先级的初始化
static int __init early_init(void)
{
pr_info("Early initialization\n");
return 0;
}
early_initcall(early_init);
static int __init core_init(void)
{
pr_info("Core initialization\n");
return 0;
}
core_initcall(core_init);
static int __init late_init(void)
{
pr_info("Late initialization\n");
return 0;
}
late_initcall(late_init);
static int __init device_init(void)
{
pr_info("Device initialization\n");
return 0;
}
device_initcall(device_init);
初始化优先级从高到低:
- early_initcall
- core_initcall
- postcore_initcall
- arch_initcall
- subsys_initcall
- fs_initcall
- device_initcall
- late_initcall
3. 模块退出清理
#include <linux/kernel.h>
static struct resource *res1;
static void *ptr1;
static int major;
// 多层错误处理
static int __init my_init(void)
{
int ret;
res1 = request_region(0x1000, 256, "mydevice");
if (!res1) {
ret = -EBUSY;
goto err_out;
}
ptr1 = kmalloc(4096, GFP_KERNEL);
if (!ptr1) {
ret = -ENOMEM;
goto err_release_region;
}
major = register_chrdev(0, "mydevice", &my_fops);
if (major < 0) {
ret = major;
goto err_free_ptr;
}
pr_info("Module loaded successfully\n");
return 0;
err_free_ptr:
kfree(ptr1);
err_release_region:
release_region(res1->start, resource_size(res1));
err_out:
pr_err("Failed to load module: %d\n", ret);
return ret;
}
static void __exit my_exit(void)
{
unregister_chrdev(major, "mydevice");
kfree(ptr1);
release_region(res1->start, resource_size(res1));
pr_info("Module unloaded\n");
}
module_init(my_init);
module_exit(my_exit);
4. 模块参数验证
#include <linux/module.h>
static int validate_param(const char *val, const struct kernel_param *kp)
{
int value;
if (kstrtoint(val, 0, &value))
return -EINVAL;
if (value < 0 || value > 100)
return -EINVAL;
return param_set_int(val, kp);
}
static const struct kernel_param_ops param_ops_validated = {
.set = validate_param,
.get = param_get_int,
};
static int validated_param = 50;
module_param_cb(validated_param, ¶m_ops_validated,
&validated_param, S_IRUGO | S_IWUSR);
5. 模块级别的互斥锁
#include <linux/mutex.h>
static DEFINE_MUTEX(module_mutex);
static void module_locked_function(void)
{
mutex_lock(&module_mutex);
// 临界区
pr_info("Function executed with module-level lock\n");
mutex_unlock(&module_mutex);
}
八、调试与故障排除
模块调试技巧
#include <linux/module.h>
#include <linux/kernel.h>
// 动态调试
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
// 条件编译调试
#ifdef DEBUG
#define dbg(fmt, ...) pr_info(fmt, ##__VA_ARGS__)
#else
#define dbg(fmt, ...) do {} while (0)
#endif
static int __init debug_init(void)
{
dbg("Debug message\n");
pr_info("Normal message\n");
return 0;
}
// Makefile 中定义DEBUG
// ccflags-y += -DDEBUG
常见问题解决
# 查看模块加载日志
dmesg | tail -n 50
# 查看模块详细信息
modinfo mymodule.ko
# 查看模块依赖
depmod -n mymodule.ko
# 强制卸载模块(慎用)
rmmod -f mymodule
# 查看所有模块
lsmod
# 查看模块参数
ls -l /sys/module/mymodule/parameters/
九、终极总结
内核模块高级特性 = 灵活 + 强大 + 可维护
- 模块参数:支持int、bool、charp、array、自定义类型
- 符号导出:EXPORT_SYMBOL、EXPORT_SYMBOL_GPL
- 版本控制:MODULE_VERSION、LINUX_VERSION_CODE条件编译
- 模块依赖:MODULE_SOFTDEP、request_module()、notifier
- 运行时管理:try_module_get()、module_put()、引用计数
- 高级特性:MODULE_ALIAS、初始化优先级、错误处理
掌握这些高级特性,让你的驱动更加专业!
1137



被折叠的 条评论
为什么被折叠?



