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

linux插入模块和删除模块

一、基础知识

        模块时一种向Linux内核添加设备驱动、文件系统及其他组件的有效方法,不需要重新编译内核或者重启系统。

内核模块具有以下优点:

        1.通过使用模块,内核发布者能够预先编译大量驱动程序,而不会使内核映像的尺寸放生膨胀。

        2.内核开发者可以将试验性的代码打包到模块中,模块可以卸载,修改代码或者重新打包后再重新装载。

1.添加和删除

        从用户角度看,模块可以通过两个不同的系统程序添加到运行的内核中。他们分别是modprobe和insmod。modprobe在识别出目标模块所依赖的模块之后,在内核也会使用insmod,在用户空间对模块的处理也是基于insmod。

        在处理该系统调用时,模块代码首先复制到内核内存中。接下来是重定位工作和解决模块中未定义的引用。因为模块使用了持久编译到内核中的函数,在模块本身编译时无法确定这些函数的地址,所以需要在这里处理未定义的引用。

        处理未解决的引用,为与内核的剩余部分协作,模块必须使用内核提供的函数。这些可能是通用的辅助函数,比如几乎内核每一部分都是用的printk或者kmalloc。

        很明显这些函数定义在内核的基础代码中,因而已经加载到内存,但是如本找到与相关函数名称匹配的地址,以便解决这些引用呢?为此,内核提供了一个所有到处函数的列表。该列表给出了所有到处函数的内存地址和对应的函数名,可以通过proc文件系统访问。即/proc/kallsyms:

场景描述

假设需要加载驱动模块A(driverA.ko),其依赖模块B(driverB.ko),操作流程如下:

步骤1:确认依赖关系
# 查看模块A的依赖信息
modinfo driverA.ko | grep depends  # 输出示例:depends: driverB

步骤2:递归加载依赖
# 加载基础依赖模块B(需使用绝对路径)
sudo insmod /lib/modules/$(uname -r)/kernel/drivers/base/driverB.ko

# 验证模块B是否加载成功
lsmod | grep driverB  # 应显示模块B信息

# 加载主模块A
sudo insmod /path/to/driverA.ko

# 验证完整加载
dmesg | tail -10  # 查看内核日志中的初始化消息

步骤3:卸载逆向操作
# 先卸载主模块A
sudo rmmod driverA

# 再卸载依赖模块B(需确保无其他模块依赖它)
sudo rmmod driverB

         在加载模块时所需要的操作,与通过ld和ld.so借助于动态库借链接应用程序的操作。从外部来看,模块只是普通的可重定位目标文件。file命令的输出表明模块文件是可重定位的,这是用户空间程序设计中一种专业术语。可重定位文件的函数都不会引用绝对地址,而只是向指向代码中的相对地址,因此可以在内存的任意偏移地址加载,当然在映像加载到内存时映像中的地址要由动态链接器ld.so进行适当的修改即可。重定位的工作有内核自身执行,而不是动态装载器。

         ramfs模块允许在内存中建立一个文件系统(成为RAM磁盘),因而需要调用register_filesystem函数将自身添加到内核中可用于文件系统的列表。此模块还使用内核代码中的generic_file_read/generic_write标准函数,基本大多数内核文件系统都会使用这些普通函数。

二、插入和删除模块

       用户空间工具和内核的模块实现之间的接口,包块两个系统调用。

  • init_module:用于将新模块插入内核,用户空间工具提供二进制数据,重定位和解决引用等工作由内核完成。
  • delete_module:功能是从内核移除模块,但要求该模块代码及其导出函数都不再被使用。
  • request_module:不是系统调用,可从内核端加载模块,还能实现热插拔功能 。

 1.模块的表示        

        module是一个非常重要的数据结构。内核中驻留的每个模块,都分配了该结构的一个实例。

        state表示该模块的当前状态,可以从枚举类型module_state取值:

 

         syms,num_syms和crc用于管理模块导出的符号。syms是一个数组,有num_syms个数组项,数组项类型为kernel_symbol,负责将标识符(name)分配到内存地址(value)

        

         在导出符号时,内核不仅考虑可以有所有模块使用符号,还要考虑只能由GPL兼容模块使用的符号。

        模块的二进制数据有两个部分 :初始化部分和核心部分

        内核提供license_is_gpl_compatible函数来判断给许可是否与GPL兼容

2.依赖关系和引用

 若模块 B 使用模块 A 提供的函数,二者就存在关联,有两种理解角度:
        一是模块 B 依赖模块 A,即模块 A 未驻留内核内存时,模块 B 无法装载;
        二是模块 B 引用模块 A,即模块 B 未移除时,模块 A 无法从内核移除,实际上需所有引用模块 A 的模块都移除才行,内核将这种关系称为模块 B 使用模块 A。为管理这些依赖关系,内核需引入新的数据结构。 

3.模块的二进制结构 

模块采用 ELF 二进制格式,包含一些普通程序或库中没有的额外段(少量编译器产生的重定位段与讨论无关)。
生成模块的步骤有三步:
        首先,将模块源代码中的所有 C 文件编译成普通的.o 目标文件;
        然后,内核分析目标文件,把找到的附加信息(如模块依赖关系)保存在独立文件中并编译为二进制文件;
        最后,把前两步产生的二进制文件链接起来,得到最终模块。

a.初始化及清理函数

        <init.h>中的module_init和module_exit宏用于定义init函数和exit函数。

b.导出符号

        内核为导出符号提供了两个宏:EXPORT_SYMBOL 和 EXPORT_SYMBOL_GPL。前者用于一般的导出符号,后者只用于 GPL 兼容代码的导出符号,目的是将相应符号放置到模块二进制映象的适当段中。

c.一般模块信息

        模块许可证、开发者和描述、备选名称、基本版本控制 

4.插入模块

        init_module系统调用是用户空间和内核之间用于装载新模块的接口,通过load_module函数将二进制数据传输到内核地址空间中:

        在实现load_module时会出现异常,内核源代码中该函数:完成所有碰到的异常问题,此函数可以完成的任务如下:

        1.从用户空间复制模块数据到内核地址空间中的一个临时内存位置

        2.查找各个段的位置

        3.确保内核和模块版本控制字符串和struct module的定义匹配问题

        4.将存在的各个段分配到其在内存中的最终位置

        5.重定位符号并解决引用,链接到模块符号任何版本控制信息都会注意到处理模块的参数。

5.删除模块 

        从内核移除模块比插入模块简单得多,系统调用delete_module函数来实现移除模块:

6.整体流程

 

在Linux内核中,模块的加载过程涉及用户空间工具与内核函数的协同工作。以下是insmod命令触发模块加载时的详细流程:


1. 用户空间操作:insmod触发系统调用

  • 当用户执行insmod example.ko时,insmod工具会:
    1. 读取模块的二进制文件(.ko)。
    2. 调用系统调用 init_module,将模块数据传递给内核。

2. 内核处理:init_module系统调用

  • init_module 是内核提供的系统调用(系统调用号由内核版本决定,如sys_init_module)。
  • 它的主要任务:
    1. 权限检查:验证用户是否有权限加载模块(需CAP_SYS_MODULE权限)。
    2. 调用load_module:将模块数据交给内核内部的load_module函数处理。

3. 内核核心函数:load_module

  • load_module 是模块加载的核心函数,负责:
    1. 解析ELF格式:检查模块的ELF头、段信息。
    2. 版本校验:确保模块与当前内核版本兼容(通过vermagic字符串)。
    3. 符号重定位:处理模块对其他模块或内核符号的引用(如调用resolve_symbol)。
    4. 分配内存:为模块的代码(.text)、数据(.data)等段分配内存。
    5. 注册模块元数据:创建struct module实例,记录模块状态、符号表等信息。
    6. 调用模块初始化函数:执行由module_init宏定义的函数(如example_init)。

4. 模块初始化:module_init函数

  • 开发者通过module_init(example_init)定义的函数example_init
    • load_module完成后被调用。
    • 负责模块特有的初始化工作(如注册设备驱动、注册文件系统等)。
    • 如果初始化失败,内核会回滚加载操作。


http://www.kler.cn/a/570530.html

相关文章:

  • 政务信息化项目命名有什么门道?
  • 【JAVA面试题】设计模式之原型模式
  • 清华DeepSeek深度探索与进阶指南
  • GEO数据挖掘
  • 下载魔塔社区模型文件
  • pymodbus简单使用
  • 深度学习-136-LangGraph之应用实例(五)构建RAG问答系统同时从不同的角度对比优化效果
  • 1分钟简化理解单体、微服务、分布式和Serverless
  • 算法系列之数据结构-二叉树
  • 通俗易懂版 Maven 科普,maven是什么?
  • GMAC网络延时性能优化
  • QT 记事本程序开发
  • RHCE9.0版本笔记3:创建、查看和编辑文本文件
  • Spark核心之03写mysql、写HBase、RDD宽窄依赖、DAG、缓存、Checkpoint
  • GSMA SAS 安全生产审计检查清单
  • ARM MTE
  • C++:类和对象(下篇)
  • 内网穿透的应用-企业级远程办公方案:NAS部署网页版Linux,HTTPS加密访问全配置
  • 【word】电子签名设置、保存和调用
  • 蓝桥杯 之 图形规律