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)的提示信息。
补充说明:
-
insmod
的核心工作流程-
读取
.ko
模块文件,验证其完整性和与当前内核的兼容性(如版本匹配、架构一致)。 -
为模块分配内核内存,将代码和数据加载到内存中。
-
解析模块中引用的内核符号(如
printk
、module_init
等),通过内核符号表找到对应的内存地址并完成链接(若符号未找到,加载失败并提示unknown symbol
错误)。 -
调用模块的初始化函数(通过
module_init
注册的函数),完成模块的启动工作。
-
-
未解析符号(unresolved symbol)的处理
模块中使用的内核函数(如
printk
)在编译时仅作为符号引用存在,并未绑定具体地址。加载时,内核会检查这些符号是否存在于内核导出的符号表(由EXPORT_SYMBOL
声明)中,只有找到匹配的符号才能完成链接。若模块使用了未导出的内核函数,insmod
会加载失败,这是内核保护自身接口的一种机制。 -
加载时配置的优势
例如,一个硬件驱动模块可能需要指定设备的 I/O 地址,通过加载时参数(如
insmod mydriver.ko io_addr=0x3f8
)设置,无需重新编译就能适配不同硬件配置,极大提升了模块的通用性。 -
与
modprobe
的区别
insmod
仅负责加载指定模块,而modprobe
会自动处理模块依赖(如加载某模块前先加载其依赖的其他模块),并从系统默认模块目录中查找模块,使用更方便(推荐优先使用modprobe
而非insmod
)。 -
sys_init_module
的核心作用作为模块加载的内核级入口,它负责模块加载的全流程:从内存分配到代码复制,再到符号解析和初始化调用。其中
vmalloc
是内核用于分配大块、非连续内存的接口,适合存放模块这类需要较大空间的代码和数据(区别于kmalloc
分配的连续内存)。 -
sys_
前缀的意义内核通过
sys_
前缀统一标识系统调用函数,方便源码管理和函数分类。例如,sys_open
对应open
系统调用,sys_read
对应read
系统调用。使用grep "sys_" kernel/
这类命令,可快速定位系统调用的实现代码。 -
modprobe
处理依赖的优势模块依赖是常见场景(如某网络驱动可能依赖通用的网络核心模块)。
modprobe
会读取/lib/modules/$(uname -r)/modules.dep
文件(该文件记录了模块间的依赖关系),自动加载所有依赖模块;而insmod
不具备依赖解析能力,仅能加载指定模块,遇到未解析符号时直接失败。 -
模块搜索路径
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
目录下找到。
补充说明:
-
rmmod
移除失败的核心场景-
模块被引用:若模块提供的资源(如设备节点、文件系统、网络接口)正被进程使用(如进程持有该模块设备的文件描述符),内核会拒绝移除,避免引发 "资源悬空" 导致程序崩溃。
-
内核配置限制 :若内核编译时启用了
CONFIG_MODULE_FORCE_REMOVE=n
(默认通常为y
,允许强制移除),或运行时通过sysctl
禁用了模块移除,rmmod
会直接失败。 -
依赖模块未移除:若其他已加载模块依赖当前模块(如 A 模块引用了 B 模块的符号),需先移除依赖模块,才能成功移除 B 模块。
-
-
lsmod
输出信息的关键含义
lsmod
输出通常包含三列核心数据:-
Module :模块名称(如
hello
); -
Size:模块占用的内核内存大小(单位:字节);
-
Used by :使用该模块的进程数或依赖模块数(若为
0
,表示模块当前未被使用,可安全移除)。
-
-
虚拟文件系统的作用
-
/proc/modules
:是内核提供的 "文本接口",实时反映模块加载状态,内容格式简单(与lsmod
输出对应),适合脚本读取。 -
/sys/module
:是sysfs
提供的 "目录化接口",每个已加载模块对应一个子目录(如/sys/module/hello
),子目录下包含模块参数、依赖关系、内存地址等更详细的结构化信息,适合精细化查询。
-