第二章:模块的编译与运行-7 Loading and Unloading Modules

In continuation of the previous text: 第二章:模块的编译与运行-6The Current process, let's GO ahead .

Loading and Unloading Modules

After the module is built, the next step is loading it into the kernel. As we've already pointed out, insmod does the job for you. The program loads the module code and data into the kernel, which, in turn, performs a function similar to that of ld, in that it links any unresolved symbol in the module to the symbol table of the kernel.
Unlike the linker, however, the kernel doesn't modify the module's disk file, but rather an in-memory copy. insmod accepts a number of command-line options (for details, see the manpage), and it can assign values to parameters in your module before linking it to the current kernel. Thus, if a module is correctly designed, it can be configured at load time; load-time configuration gives the user more flexibility than compile-time configuration, which is still used sometimes. Load-time configuration is explained in the section "Module Parameters," later in this chapter.

模块构建完成后,下一步是将其加载到内核中。正如我们已经提到的,insmod 命令可以完成这项工作。该程序将模块的代码和数据加载到内核中,内核随后会执行类似 ld(链接器)的功能 , 将模块中所有未解析的符号与内核的符号表进行链接。

不过,与链接器不同的是,内核不会修改磁盘上的模块文件,只会修改内存中的副本。insmod 接受多个命令行选项(详情参见手册页),并且可以在将模块链接到当前内核之前,为模块中的参数赋值。因此,如果模块设计合理,就可以在加载时进行配置;加载时配置比编译时配置更具灵活性(尽管编译时配置有时仍会被使用)。关于加载时配置的详细说明,参见本章后续的 "模块参数" 部分。

Interested readers may want to look at how the kernel supports insmod: it relies on a system call defined in kernel/module.c. The function sys_init_module allocates kernel memory to hold a module (this memory is allocated with vmalloc; see the section "vmalloc and Friends" in Chapter 8); it then copies the module text into that memory region, resolves kernel references in the module via the kernel symbol table, and calls the module's initialization function to get everything going.

感兴趣的读者可能想了解内核是如何支持 insmod 的:它依赖于 kernel/module.c 中定义的一个系统调用。sys_init_module 函数会分配内核内存来存放模块(此内存通过 vmalloc 分配,详见第 8 章的 "vmalloc 及其相关函数" 部分);随后将模块代码复制到该内存区域,通过内核符号表解析模块中的内核引用,并调用模块的初始化函数来启动整个模块。

If you actually look in the kernel source, you'll find that the names of the system calls
are prefixed with sys_. This is true for all system calls and no other functions; it's
useful to keep this in mind when grepping for the system calls in the sources.

如果你实际查看内核源码,会发现所有系统调用的名称都以 sys_ 为前缀。所有系统调用均遵循此规则,且无其他函数会使用该前缀;在源码中搜索系统调用时,记住这一点会很有帮助。

The modprobe utility is worth a quick mention. modprobe, like insmod, loads a module into the kernel. It differs in that it will look at the module to be loaded to see whether it references any symbols that are not currently defined in the kernel. If any such references are found, modprobe looks for other modules in the current module search path that define the relevant symbols. When modprobe finds those modules (which are needed by the module being loaded), it loads them into the kernel as well.
If you use insmod in this situation instead, the command fails with an "unresolved symbols" message left in the system logfile.

值得简要提及的工具是 modprobe。与 insmod 类似,modprobe 也用于将模块加载到内核中,但二者存在区别:modprobe 会先检查待加载模块,查看其是否引用了当前内核中未定义的符号。若发现此类引用,modprobe 会在当前模块搜索路径中查找定义了相关符号的其他模块。当找到这些模块(即待加载模块的依赖模块)时,modprobe 会将它们也一并加载到内核中。

若在这种存在依赖的场景下使用 insmod,命令会执行失败,且系统日志文件中会留下 "未解析符号"(unresolved symbols)的提示信息。

补充说明:

  1. insmod 的核心工作流程

    • 读取 .ko 模块文件,验证其完整性和与当前内核的兼容性(如版本匹配、架构一致)。

    • 为模块分配内核内存,将代码和数据加载到内存中。

    • 解析模块中引用的内核符号(如 printkmodule_init 等),通过内核符号表找到对应的内存地址并完成链接(若符号未找到,加载失败并提示 unknown symbol 错误)。

    • 调用模块的初始化函数(通过 module_init 注册的函数),完成模块的启动工作。

  2. 未解析符号(unresolved symbol)的处理

    模块中使用的内核函数(如 printk)在编译时仅作为符号引用存在,并未绑定具体地址。加载时,内核会检查这些符号是否存在于内核导出的符号表(由 EXPORT_SYMBOL 声明)中,只有找到匹配的符号才能完成链接。若模块使用了未导出的内核函数,insmod 会加载失败,这是内核保护自身接口的一种机制。

  3. 加载时配置的优势

    例如,一个硬件驱动模块可能需要指定设备的 I/O 地址,通过加载时参数(如 insmod mydriver.ko io_addr=0x3f8)设置,无需重新编译就能适配不同硬件配置,极大提升了模块的通用性。

  4. modprobe 的区别
    insmod 仅负责加载指定模块,而 modprobe 会自动处理模块依赖(如加载某模块前先加载其依赖的其他模块),并从系统默认模块目录中查找模块,使用更方便(推荐优先使用 modprobe 而非 insmod)。

  5. sys_init_module 的核心作用

    作为模块加载的内核级入口,它负责模块加载的全流程:从内存分配到代码复制,再到符号解析和初始化调用。其中 vmalloc 是内核用于分配大块、非连续内存的接口,适合存放模块这类需要较大空间的代码和数据(区别于 kmalloc 分配的连续内存)。

  6. sys_ 前缀的意义

    内核通过 sys_ 前缀统一标识系统调用函数,方便源码管理和函数分类。例如,sys_open 对应 open 系统调用,sys_read 对应 read 系统调用。使用 grep "sys_" kernel/ 这类命令,可快速定位系统调用的实现代码。

  7. modprobe 处理依赖的优势

    模块依赖是常见场景(如某网络驱动可能依赖通用的网络核心模块)。modprobe 会读取 /lib/modules/$(uname -r)/modules.dep 文件(该文件记录了模块间的依赖关系),自动加载所有依赖模块;而 insmod 不具备依赖解析能力,仅能加载指定模块,遇到未解析符号时直接失败。

  8. 模块搜索路径
    modprobe 的默认搜索路径是 /lib/modules/$(uname -r)/,该目录存放了系统已安装的内核模块。若需让 modprobe 找到自定义模块,可将 .ko 文件复制到该目录下,或通过 modprobe --dirname 指定自定义路径。

As mentioned before, modules may be removed from the kernel with the rmmod utility. Note that module removal fails if the kernel believes that the module is still in use (e.g., a program still has an open file for a device exported by the modules), or if the kernel has been configured to disallow module removal. It is possible to configure the kernel to allow "forced" removal of modules, even when they appear to be busy. If you reach a point where you are considering using this option, however, things are likely to have gone wrong badly enough that a reboot may well be the better course of action. The lsmod program produces a list of the modules currently loaded in the kernel. Some other information, such as any other modules making use of a specific module, is also provided. lsmod works by reading the /proc/modules virtual file. Informa- tion on currently loaded modules can also be found in the sysfs virtual filesystem under /sys/module.

正如之前提到的,可通过 rmmod 工具从内核中移除模块。需要注意的是,若内核判定模块仍在使用中(例如某程序仍打开着该模块导出的设备文件),或内核配置为禁止模块移除,移除操作会失败。

内核可配置为允许 "强制" 移除模块,即便模块看似处于忙碌状态。但如果到了需要考虑使用该选项的地步,通常意味着系统已出现严重问题,此时重启系统很可能是更稳妥的选择。

lsmod 程序会列出当前内核中已加载的所有模块,还会提供其他信息(如哪些模块正在使用某个特定模块)。lsmod 的工作原理是读取 /proc/modules 虚拟文件,当前已加载模块的信息也可在 sysfs 虚拟文件系统的 /sys/module 目录下找到。

补充说明:

  1. rmmod 移除失败的核心场景

    • 模块被引用:若模块提供的资源(如设备节点、文件系统、网络接口)正被进程使用(如进程持有该模块设备的文件描述符),内核会拒绝移除,避免引发 "资源悬空" 导致程序崩溃。

    • 内核配置限制 :若内核编译时启用了 CONFIG_MODULE_FORCE_REMOVE=n(默认通常为 y,允许强制移除),或运行时通过 sysctl 禁用了模块移除,rmmod 会直接失败。

    • 依赖模块未移除:若其他已加载模块依赖当前模块(如 A 模块引用了 B 模块的符号),需先移除依赖模块,才能成功移除 B 模块。

  2. lsmod 输出信息的关键含义
    lsmod 输出通常包含三列核心数据:

    • Module :模块名称(如 hello);

    • Size:模块占用的内核内存大小(单位:字节);

    • Used by :使用该模块的进程数或依赖模块数(若为 0,表示模块当前未被使用,可安全移除)。

  3. 虚拟文件系统的作用

    • /proc/modules:是内核提供的 "文本接口",实时反映模块加载状态,内容格式简单(与 lsmod 输出对应),适合脚本读取。

    • /sys/module:是 sysfs 提供的 "目录化接口",每个已加载模块对应一个子目录(如 /sys/module/hello),子目录下包含模块参数、依赖关系、内存地址等更详细的结构化信息,适合精细化查询。

相关推荐
---学无止境---4 小时前
Linux中驱动程序通过fasync异步通知应用程序的实现
linux
cccyi74 小时前
Linux 进程间通信机制详解
linux·进程通信
北京迅为4 小时前
【北京迅为】iTOP-4412精英版使用手册-第三十五章 WEB控制LED
linux·嵌入式硬件·嵌入式·4412
让我们一起加油好吗4 小时前
【C++】封装红黑树模拟实现 set 和 map
linux·c++·set·map·红黑树
暴富奥利奥4 小时前
完成docker方式的ros环境配置
linux·学习·docker·容器
秃头菜狗4 小时前
十四、运行经典案例 wordcount
大数据·linux·hadoop
望获linux5 小时前
【实时Linux实战系列】实时系统的可观测性:Prometheus 与 Grafana 集成
大数据·linux·服务器·开发语言·网络·操作系统
hweiyu005 小时前
Linux 命令:mount
linux·运维·服务器
zhmy_0065 小时前
linux 多服务器下目录数据文件实时同步
linux·文件实时同步