Linux 核心--13.Linux动态模块( 二 )




12.1模块的加载



图12.1 核心模块链表s


核心模块的加载方式有两种 。首先一种是使用insmod命令手工加载模块 。另外一种则是在需要时加载模块;我们称它为请求加载 。当核心发现有必要加载某个模块时 , 如用户安装了核心中不存在的文件系统时 , 核心将请求核心后台进程(kerneld)准备加载适当的模块 。这个核心后台进程仅仅是一个带有超级用户权限的普通用户进程 。当系统启动时它也被启动并为核心打开了一个进程间通讯(IPC)通道 。核心需要执行各种任务时用它来向kerneld发送消息 。

kerneld的主要功能是加载和卸载核心模块, 但是它还可以执行其他任务, 如通过串行线路建立PPP连接并在适当时候关闭它 。kerneld自身并不执行这些任务 , 它通过某些程序如insmod来做此工作 。它只是核心的代理 , 为核心进行调度 。

insmod程序必须找到要求加载的核心模块 。请求加载核心模块一般被保存在/lib/modules/kernel-version 中 。这些核心模块和系统中其他程序一样是已连接的目标文件 , 但是它们被连接成可重定位映象 。即映象没有被连接到在特定地址上运行 。这些核心模块可以是a.out或ELF文件格式 。insmod将执行一个特权级系统调用来找到核心的输出符号 。这些都以符号名以及数值形式 , 如地址值成对保存 。核心输出符号表被保存在核心维护的模块链表的第一个module结构中 , 同时module_list指针指向此结构 。只有特殊符号被添加到此表中 , 它们在核心编译与连接时确定 , 不是核心每个符号都被输出到其模块中 。例如设备驱动为了控制某个特定系统中断而由核心例程调用的"request_irq"符号 。在我的系统中 , 其值为0x0010cd30 。我们可以通过使用ksyms工具或者查看/proc/ksyms来观看当前核心输出符号 。ksyms工具既可以显示所有核心输出符号也可以只显示那些已加载模块的符号 。insmod将模块读入虚拟内存并通过使用来自核心输出符号来修改其未解析的核心例程和资源的引用地址 。这些修改工作采取由insmod程序直接将符号的地址写入模块中相应地址来修改内存中的模块映象 。

当insmod修改完模块对核心输出符号的引用后 , 它将再次使用特权级系统调用来申请足够的空间来容纳新核 心 。核心将为其分配一个新的module结构以及足够的核心内存来保存新模块, 并将它放到核心模块链表的尾部 。然后将其新模块标志为UNINITIALIZED 。

图12.1给出了一个加载两个模块:VFAT和FAT后的核心链表示意图 。不过图中没有画出链表中的第一个模块: 用来存放核心输出符号表的一个伪模块 。lsmod可以帮助我们列出系统中所有已加载的核心模块以及相互间 依赖关系 。它是通过重新格式化从核心module结构中建立的/proc/modules来进行这项工作的 。核心为其分配的内存被映射到insmod的地址空间, 这样它就能访问核心空间 。insmod将模块拷贝到已分配空间中, 如果为它分配的核心内存已用完 , 则它将再次申请 。不过不要指望多次将加载模块到相同地址 , 更不用说在两个不同 Linux系统的相同位置 。另外此重定位工作包括使用适当地址来修改模块映象 。

这个新模块也希望将其符号输出到核心中 , insmod将为其构造输出符号映象表 。每个核心模块必须包含模块 初始化和模块清除例程 , 它们的符号被设计成故意不输出, 但是insmod必须知道这些地址, 这样它可以将它们传递给核心 。所有这些工作做完之后 , insmod将调用初始化代码并执行一个特权级系统调用将模块的初始化与清除例程地址传递给核心 。

当将一个新模块加载到核心中间时 , 核心必须更新其符号表并修改那些被新模块使用的老模块 。那些依赖于其他模块的模块必须维护在其符号表尾部维护一个引用链表并在其module数据结构中指向它 。图12.1中VFAT 依赖于FAT文件系统模块 。所以FAT模块包含一个对VFAT模块的引用;这个引用在加载VFAT模块时添加 。核心调用模块的初始化例程 , 如果成功它将安装此模块 。模块的清除例程地址被存储在其module结构中 , 它将在 模块卸载时由核心调用 。最后模块的状态被设置成RUNNING 。

推荐阅读