首页 百科知识 模块的加载

模块的加载

时间:2022-10-10 百科知识 版权反馈
【摘要】:本节主要描述Linux内核动态加载功能模块。Linux可以让用户随意动态的加载与卸载操作系统部件。内核应该使用严格的版本控制来对加载模块进行检查以防止这种情况的发生。内核模块的加载方式有两种。首先一种是使用insmod命令手工加载模块;另外一种则是在需要时加载模块,称它为请求加载。在有的系统中,其值为0x0010cd30。当insmod修改完模块对内核输出符号的引用后,它将再次使用特权级系统调用来申请足够的空间来容纳新内核。

5.3.1 模块的加载

本节主要描述Linux内核动态加载功能模块(如文件系统的工作原理)。Linux内核是一种monolithic类型的内核,即单一的大程序,内核中所有的功能部件都可以对其全部内部数据结构和例程进行访问。内核的另外一种形式是微内核结构,此时内核的所有功能部件都被拆成独立部分,这些部分之间通过严格的通信机制进行联系。这样通过配置进程将新部件加入内核的方式非常耗时。比如说用户想为一个NCR 810SCSI卡配置SCSI驱动,但是内核中没有这个部分。那么我们必须重新配置并重构内核。Linux可以让用户随意动态的加载与卸载操作系统部件。Linux模块就是这样一种可在系统启动后的任何时候动态连入内核的代码块。当不再需要它时又可以将它从内核中卸载并删除。Linux模块多指设备驱动、伪设备驱动,如网络设备和文件系统。

Linux为用户提供了两个命令:使用insmod来显式加载内核模块,使用rmmod来卸载模块。同时内核自身也可以请求内核后台进程kerneld来加载与卸载模块。

动态可加载代码的好处在于可以让内核保持很小的尺寸同时非常灵活。在Intel系统中由于使用了模块,整个内核仅为406KB长。由于只是偶尔使用VFAT文件系统,所以将Linux内核构造成当mount VFAT分区时自动加载VFAT文件系统模块。当卸载VFAT分区时系统将检测到不再需要VFAT文件系统模块,将把它从系统中卸载。模块同时还可以让用户无需重构内核并频繁重新启动来尝试运行新内核代码。尽管使用模块很自由,但是也有可能同时带来与内核模块相关的性能与内存损失。可加载模块的代码一般有些长并且额外的数据结构可能会占据一些内存。同时对内核资源的间接使用可能带来一些效率问题。

一旦Linux模块被加载则它和普通内核代码一样都是内核的一部分。它们具有与其他内核代码相同的权限与职责;换句话说Linux内核模块可以像所有内核代码和设备驱动一样使内核崩溃。

模块为了使用所需内核资源所以必须能够找到它们。例如模块需要调用内核内存分配例程kmalloc()来分配内存。模块在构造时并不知道kmalloc()在内存中何处,这样内核必须在使用这些模块前修改模块中对kmalloc()的引用地址。内核在其内核符号表中维护着一个内核资源链表,这样当加载模块时它能够解析出模块中对内核资源的引用。Linux还允许存在模块堆栈,它在模块之间相互调用时使用。例如VFAT文件系统模块可能需要FAT文件系统模块的服务,因为VFAT文件系统多少是从FAT文件系统中扩展而来。某个模块对其他模块的服务或资源的需求类似于模块对内核本身资源或服务的请求。不过此时所请求的服务是来自另外一个事先已加载的模块。每当加载模块时,内核将把新近加载模块输出的所有资源和符号添加到内核符号表中。

当试图卸载某个模块时,内核需要知道此模块是否已经没有被使用,同时它需要有种方法来通知此模块将被卸载。模块必须能够在从内核删除之前释放其分配的所有系统资源,如内核内存或中断。当模块被卸载时,内核将从内核符号表中删除所有与之对应的符号。

可加载模块具有使操作系统崩溃的能力,而编写较差的模块会带来另外一种问题。当你在一个或早或迟构造的内核而不是当前你运行的内核上加载模块时将会出现什么结果?一种可能的情况是模块将调用具有错误参数的内核例程。内核应该使用严格的版本控制来对加载模块进行检查以防止这种情况的发生。

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

img202

图5-2 内核模块链表s

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

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

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈