有两种装入模块的方法,第一种是用insmod命令人工把模块插入到内核,第二种是一种更灵活的方法,当需要时装入模块,这就是所谓的请求装入。
当内核发现需要一个模块时,例如,用户安装一个不在内核的文件系统时,内核将请求内核守护进程(kerneld)装入一个合适的模块。
内核守护进程(kerneld)是一个标准的用户进程,但它具有超级用户权限。Kerneld通常是在系统启动时就开始执行,它打开IPC(Inter-Process Communication)到内核的通道,内核通过给kerneld发送消息请求执行各种任务。
Kerneld的主要功能是装入和卸载内核模块,但它也具有承担其它任务的能力,例如,当必要时,通过串行链路启动PPP链路,不需要时,则关闭它。Kerneld并不执行这些任务,它通过运行诸如insmod这样的程序来做这些工作,kerneld仅仅是内核的一个代理。
insmod 实用程序必须找到请求装入的内核模块,请求装入的内核模块通常保存在/lib/modules/kernel-version/目录下。内核模块被连接成目标文件,与系统中其它程序不同的是,这种目标文件是可重定位的(它们是a.out或elf格式的目标文件)。insmods实用程序位于/sbin目录下,该程序执行以下操作:
(1) 从命令行中读取要装入的模块名。
(2) 确定模块代码所在的文件在系统目录树中的位置,即/lib/modules/kernel-version/目录。
(3) 计算存放模块代码、模块名和module结构所需要的内存区大小。调用create_module( )系统调用,向它传递新模块的模块名和大小。
(4) 用QM_MODULES子命令反复调用query_module( )系统调用来获得所有已安装模块的模块名。
(5) 用QM_SYMBOL子命令反复调用query_module( )系统调用来获得内核符号表和所有已经安装到内核的模块的符号表。
(6) 使用内核符号表、模块符号表以及create_module( )系统调用所返回的地址重新定位该模块文件中所包含的文件的代码。这就意味着用相应的逻辑地址偏移量来替换所有出现的外部符号和全局符号。
(7) 在用户态地址空间中分配一个内存区,并把module结构、模块名以及为正在运行的内核所重定位的模块代码的一个拷贝装载到这个内存区中。如果该模块定义了init_module( )函数,那么module结构的init域就被设置成该模块的init_module( )函数重新分配的地址。同理,如果模块定义了cleanup_module( )函数,那么cleanup域就被设置成模块的cleanup_module( )函数所重新分配的地址。
(8) 调用init_module( )系统调用,向它传递上一步中所创建的用户态的内存区地址。
(9) 释放用户态内存区并结束。
为了取消模块的安装,用户需要调用/sbin/rmmod实用程序,它执行以下操作:
(1) 从命令行中读取要卸载的模块的模块名。
(2) 使用QM_MODULES子命令调用query_module( )系统调用来取得已经安装的模块的链表。
(3) 使用QM_REFS子命令多次调用query_module( )系统调用来检索已安装的模块间的依赖关系。如果一个要且在的模块上面还安装有某一模块,就结束。
(4) 调用delete_module( )系统调用,向其传递模块名。