当前位置: 首页 > ops >正文

【奔跑吧!Linux 内核(第二版)】第5章:内核模块

笨叔 陈悦. 奔跑吧 Linux 内核(第2版) [M]. 北京: 人民邮电出版社, 2020.

文章目录

  • 从一个内核模块开始
  • 模块参数
  • 符号共享

Linux 内核采用了宏内核架构,操作系统的大部分功能在内核中实现,比如进程管理、内存管理、进程调度、设备管理等,并且在特权模式下(内核空间中)运行。Linux 的这种宏内核可以理解为完全静态的内核,那么如何实现运行时内核的动态扩展呢?其实 Linux 内核在发展过程中早就引入了内核模块的这种机制,可在内核运行时加载一组目标代码来实现某个特定的功能,这样在实际使用 Linux 的过程中就不需要重新编译内核代码来实现动态扩展。

从一个内核模块开始

内核在初始化各个模块时有优先级顺序。对于驱动模块来说,它的优先级不是特别高,而且内核把所有模块的初始化函数都存放在一个特别的段中来管理。

通过 file 命令检查编译的模块是否正确,通过 modinfo 命令进一步检查,lsmod 命令查看当前模块是否已经被加载到系统中。

模块参数

为了根据不同的应用场景给内核模块传递不同的参数,Linux 内核提供了一个宏实现模块的参数传递。module_param是 Linux 内核模块编程中的一个关键宏,用于在加载模块时传递参数,从而动态配置模块行为。

module_param(name, type, perm);
  • ​​name​​:模块内定义的变量名,也是加载模块时使用的参数名(如 insmod module.ko name=value)。
  • type​​:参数类型,常见选项包括: 整型:int、uint、long、ulong等。字符串:charp(字符指针,自动分配内存)。 布尔型:bool(0/1)、invbool(值反转)。
  • ​​perm​​:权限掩码,使用 <linux/stat.h>中的宏(如 0644表示用户可读写,组和其他用户只读)。

示例代码

#include <linux/module.h>
#include <linux/moduleparam.h>static char *msg = "hello";
static int count = 1;
module_param(count, int, 0644);
module_param(msg, charp, 0444);static int __init init_func(void) {for (int i = 0; i < count; i++)printk(KERN_INFO "%s\n", msg);return 0;
}
module_init(init_func);
  • ​​加载命令​​:insmod example.ko msg="world" count=3
  • 输出​​:通过 dmesg查看内核日志,打印 3 次 “world”

说明:

  • 默认值​​:若未传递参数,使用模块内定义的初始值。
  • 动态修改​​:通过 /sys/module/模块名/parameters/文件可运行时修改参数(需写权限)。
  • 文档描述​​:配合 MODULE_PARM_DESC宏生成参数说明,通过 modinfo查看。

符号共享

为了在同一驱动程序的不同内核模块中实现函数的相互调用、参数的访问,Linux 内核为我们提供了对应的宏。

符号(Symbol)在内核中指的是函数或全局变量的名称,每个符号对应内存中的一个地址:函数名对应代码段中的起始地址,变量名对应数据段中的存储位置。内核模块符号共享的核心思想是:一个模块可以将自己的函数或变量"导出",供其他模块使用,这类似于工厂中不同车间共享工具的场景。

内核维护着一个全局的符号表(本质是哈希表),记录了所有导出符号的名称和地址。当模块A导出符号后,这些符号会被注册到这个公共表中,模块B就可以通过名称找到并使用它们。

导出宏的使用

Linux内核提供了两个主要的宏用于符号导出:

​​1. EXPORT_SYMBOL(sym)​​:将符号对全部内核代码公开,允许所有模块使用(无论许可证)。
2. EXPORT_SYMBOL_GPL(sym)​​:仅允许GPL兼容许可证的模块使用导出的符号。

推荐做法是:除非必要,优先使用EXPORT_SYMBOL_GPL,保证内核许可证纯洁性。

导出步骤

  1. 定义符号。在模块中定义要导出的函数或全局变量。
int my_crc32(const unsigned char *buf, size_t len) {// CRC32计算实现return crc;
}
int global_counter = 0;
  1. 导出符号。使用导出宏公开符号。
EXPORT_SYMBOL(my_crc32);
EXPORT_SYMBOL(global_counter);
  1. 使用符号。在其他模块中先声明符号(类似extern),再直接使用。
extern int my_crc32(const unsigned char *buf, size_t len);
extern int global_counter;static int __init use_module_init(void) {int crc = my_crc32("hello", 5);global_counter++;return 0;
}

符号重复可能导致的问题包括

  1. 链接错误,使程序无法正常构建
  2. 模块加载失败,内核拒绝加载有符号冲突的模块
  3. 运行时不可预测的行为,特别是当链接器"静默"选择了一个不符合预期的符号定义时

如何避免符号重复

  1. ​​最小暴露原则​​:尽可能限制符号的可见性,只导出真正必要的接口
  2. 命名规范化​​:使用模块前缀避免命名冲突
  3. 静态化内部符号​​:使用static关键字限制非共享符号的作用域
  4. 版本控制​​:利用内核的CRC校验机制确保符号兼容性
  5. 工具辅助​​:使用符号检查工具和构建系统配置早期发现问题
  6. 动态解析​​:对于可选依赖,考虑使用动态符号查找
  7. 文档记录​​:为导出的符号提供清晰的文档说明
http://www.xdnf.cn/news/16386.html

相关文章:

  • 关于“PromptPilot” 之2 -目标系统:Prompt构造器
  • Linux c网络专栏第三章DPDK
  • Rust与Java DynamoDB、MySQL CRM、tokio-pg、SVM、Custors实战指南
  • UV: 下一代 Python 包管理工具
  • Unity 实时 CPU 使用率监控
  • 前缀和-560.和为k的子数组-力扣(LeetCode)
  • XFile 系统架构设计文档
  • iOS安全和逆向系列教程 第20篇:Objective-C运行时机制深度解析与Hook技术
  • 七、搭建springCloudAlibaba2021.1版本分布式微服务-skywalking9.0链路追踪
  • 前端基础班学习路线
  • GPGPU基本概念
  • PiscCode实现从图像到字符艺术
  • Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现PCB上二维码检测识别(C#代码UI界面版)
  • 北大区块链技术与应用 笔记
  • 虚拟机ubuntu20.04共享安装文件夹
  • AI驱动的金融推理:Fin-R1模型如何重塑行业决策逻辑
  • docker搭建部署 onlyoffice 实现前端集成在线解析文档解决方案
  • elasticsearch 倒排索引原理详解
  • LeetCode 923.多重三数之和
  • 面试150 数字范围按位与
  • 第六章 JavaScript 互操(3)JS调用.NET
  • Ubuntu服务器安装与运维手册——操作纯享版
  • 算法竞赛阶段二-数据结构(37)数据结构动态链表list
  • CLAP文本-音频基础模型: LEARNING AUDIO CONCEPTS FROM NATURAL LANGUAGE SUPERVISION
  • 机器学习的算法有哪些?
  • Jmeter的元件使用介绍:(八)断言器详解
  • Android网络框架封装 ---> Retrofit + OkHttp + 协程 + LiveData + 断点续传 + 多线程下载 + 进度框交互
  • 【C++】论如何封装红黑树模拟实现set和map
  • haproxy七层代理(知识点+相关实验部署)
  • 面试150 只出现一次的数字Ⅱ