第9章 模块的动态加载和系统配置

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

第9章模块的动态加载和系统配置第9章模块的动态加载和系统配置本章介绍了Linux内核动态加载功能模块的工作原理。分析了Linux内核中的系统配置结构,解释了Makefile和配置文件的格式以及配置语句的含义。最后给出一个简单的例子,说明如何将自行开发的代码加入到Linux内核中。9.1模块的动态加载操作系统通常由内核和一些系统服务程序(命令解释、库文件、链接和编译程序等)组成。内核是操作系统的灵魂,它为用户进程提供了一个虚拟机接口。用户进程可以并行运行、公平的占用系统资源而互不干扰。9.1模块的动态加载操作系统通常由内核和一些系统服务程序(命令解释、库文件、链接和编译程序等)组成。内核是操作系统的灵魂,它为用户进程提供了一个虚拟机接口。用户进程可以并行运行、公平的占用系统资源而互不干扰。从结构上来分,可将操作系统分为微内核结构和单块结构两类。WindowsNT和MINIX是典型的微内核操作系统,而Linux则是单块结构的操作系统。微内核结构可方便地在系统中添加新的组件,而单块结构却不容易做到这一点。为此,Linux系统使用可动态加载和卸载的内核模块(LoadableKernelModules,LKMs),可方便地在内核中添加新的组件或卸载不再需要的内核组件。Linux使用insmod来显式加载内核模块,使用rmmod来卸载模块。同时内核自身也可以请求内核后台进程kerneld来加载与卸载模块。Linux模块大多数是设备驱动程序以及伪设备驱动程序模块,如网络设备和文件系统等。动态可加载代码的优点是可以让内核保持很小的尺寸并非常灵活。模块机制可以无需重构内核并频繁重新启动来尝试运行新内核代码。用户可以根据自己系统的需要构筑自己的私有内核。Linux源码的公开更是为改造其内核、重建有特殊要求的操作系统提供了可能。模块必须能够找到其需要使用的内核资源。例如模块需要分配内存时,要调用内核的内存分配例程kmalloc()。但在构造模块时并不知道kmalloc()在内存中何处,这样内核必须在使用这些模块前修改模块中对kmalloc()的引用地址。内核在其内核符号表中维护着一个内核资源链表这样当加载模块时它能够解析出模块中对内核资源的引用。Linux还允许存在模块堆栈,它在模块之间相互调用时使用。例如,因为VFAT(VirtuaIFileAllocationTable)文件系统是从FAT(FileAllocationTable)文件系统中扩展而来,VFAT文件系统模块可能需要FAT文件系统模块的服务。某个模块对其他模块的服务或资源的需求类似于模块对内核本身资源或服务的请求。不过此时所请求的服务是来自另外一个事先已加载的模块。当加载模块时,内核就把新近加载模块输出的所有资源和符号添加到内核符号表(Kernel-Symbol-Table)中。在/proc/ksyms.里面的每一个表项代表着一个公共的内核符号,这就是内核符号表。这些内核符号是可以被LKM引用的。LKM中所存取的每一个符号(像函数名)也会被列在这个文件里面。在该文件中可以看到LKM到底可以调用那些函数。当试图卸载某个模块时,内核需要知道此模块是否已经没有被使用,同时它需要有种方法来通知此将卸载模块。模块必须能够在从内核种删除之前释放其分配的所有系统资源,如内核内存或中断。当模块被卸载时,内核将从内核符号表中删除所有与之对应的符号。但是,内核模块的引入也带来了如下问题:但是,内核模块的引入也带来了如下问题:(1)有可能同时带来与内核模块相关的性能与内存损失。可加载模块的代码一般较长,且额外的数据结构可能会占据一些内存,对内核资源的间接使用也可能带来效率方面的问题。对系统性能和内存利用有负面影响;但是,内核模块的引入也带来了如下问题:(1)有可能同时带来与内核模块相关的性能与内存损失。可加载模块的代码一般较长,且额外的数据结构可能会占据一些内存,对内核资源的间接使用也可能带来效率方面的问题。对系统性能和内存利用有负面影响;(2)一旦Linux模块被加载,则和普通内核代码一样成为内核的一部分,具有与其他内核代码相同的权限与职责。因此,Linux内核模块也可以象所有内核代码和设备驱动一样使内核崩溃;但是,内核模块的引入也带来了如下问题:(1)有可能同时带来与内核模块相关的性能与内存损失。可加载模块的代码一般较长,且额外的数据结构可能会占据一些内存,对内核资源的间接使用也可能带来效率方面的问题。对系统性能和内存利用有负面影响;(2)一旦Linux模块被加载,则和普通内核代码一样成为内核的一部分,具有与其他内核代码相同的权限与职责。因此,Linux内核模块也可以象所有内核代码和设备驱动一样使内核崩溃;(3)为了内核模块访问所有内核资源,内核必须维护符号表,并在装入和卸载模块时修改这些符号表;但是,内核模块的引入也带来了如下问题:(1)有可能同时带来与内核模块相关的性能与内存损失。可加载模块的代码一般较长,且额外的数据结构可能会占据一些内存,对内核资源的间接使用也可能带来效率方面的问题。对系统性能和内存利用有负面影响;(2)一旦Linux模块被加载,则和普通内核代码一样成为内核的一部分,具有与其他内核代码相同的权限与职责。因此,Linux内核模块也可以象所有内核代码和设备驱动一样使内核崩溃;(3)为了内核模块访问所有内核资源,内核必须维护符号表,并在装入和卸载模块时修改这些符号表;(4)有些模块要求利用其他模块的功能,因此,内核要维护模块之间的依赖性。(5)内核必须能够在卸载模块时通知模块,并且要释放分配给模块的内存和中断等资源;(6)内核版本和模块版本的不兼容,也可能导致系统崩溃,因此,严格的版本检查是必需的。9.1.1模块的加载9.1.1模块的加载有两种方法可用来加载模块:(1)利用insmod命令手工将模块插入内核;9.1.1模块的加载有两种方法可用来加载模块:(1)利用insmod命令手工将模块插入内核;(2)由内核在必要时加载模块,称为“需求加载”。当内核发现有必要加载某个模块时,如用户安装了内核中不存在的文件系统时,内核将请求内核后台进程(kerneld)准备加载适当的模块。这个内核后台进程仅仅是一个带有超级用户权限的普通用户进程。当系统启动时它也被启动并为内核打开了一个进程间通讯(IPC)通道。内核可以利用该通道向Kerneld进程发送任务的执行请求。Kerneld进程的主要功能是加载和卸载模块,另外,该进程也负责其他一些任务,例如打开和关闭PPP链接等。kerneld自身并不执行这些任务,它通过某些程序如insmod来做此工作。因此,该进程实际是代表内核进行调度的代理。执行insmod命令时,必须指定要加载模块的位置;对需求加载的内核模块,通常保存在/lib/modules/kernel-version。和系统的其他程序一样,内核模块实际是经连接的目标文件,但模块是可重定位的,也就是说,为了让装入的模块和已有的内核组件之间可以互相访问,模块不能连接为从特定地址执行的映像文件。模块可以是a.out或elf格式的目标文件。insmod利用一个特权系统调用,可找到内核的导出符号表,符号成对出现,一个是符号名称,另外一个是符号的值,例如符号的地址。内核维护一个由module_list指针指向的module链表,其中第一个module数据结构保存有内核的导出符号表(见图9.1)。并不是所有的内核符号均在符号表中导出,而只有一些特殊的符号才被添加到符号表中。例如,“request_irq”是一个导出符号,它是一个内核例程,可由驱动程序申请控制某个特定的系统中断。利用ksyms命令或查看/proc/ksyms文件内容,可非常方便地看到所有的内核导出符号及其符号值。利用ksyms命令,不仅可以看到内核的所有符号,也可以看到只由以加载模块导出的符号。insmod命令将模块读到它本身的虚拟内存中,然后利用内核导出的符号表,修正尚未解析的对内核例程的引用。这种修正实际是对模块在内存中的映像进行修正,insmod将符号的地址写入模块中适当的位置而实现修正。图9.1装入VFAT和FAT之后的内核模块表insmod命令修正模块对内核符号的引用之后,再次利用特权系统调用请求内核分配足够的物理内存空间保存新的模块。内核将分配新的module数据结构以及足够的内核内存,并将新模块添加在内核模块表的末尾。新的内核模块标记为Uninitialized(未初始化)。图9.1是装入VFAT和FAT模块之后的内核模块表。图中并没有表示出第一个模块,它实际是一个伪模块,仅仅用来保存内核的导出符号表。利用lsmod命令可列出所有已加载的内核模块以及它们的内在依赖性。内核为新模块分配的内核内存映射到insmod进程的地址空间中,这样,insmod就可以将模块复制到新分配的内存中。insmod还对模块进行重新定位,经重定位之后,新的模块就可以从新分配的内核地址开始运行了。显然,模块不能期望自己能够在不同的Linux系统,或前后两次装入时被加载到相同地址,重定位操作可通过对模块映像中适当的地址进行修正而解决这一问题。新的模块也要向内核导出符号,由insmod建立相应的符号表。每个内核模块必须包含模块的初始化和清除例程,这些例程作为每个模块均具备的例程而不被导出,但insmod必须知道它们的地址,并将地址传递给内核。insmod同样利用特权系统调用将模块的初始化和清除例程地址传递给内核。新的模块添加到内核之后,它必须更新内核符号集并修改使用新模块的模块。由其他模块依赖的模块必须在自身符号表的末尾维护一个引用表,并指向其他模块的module结构。例如,图9.1表明VFAT文件系统模块依赖于FAT文件系统模块,因此,FAT模块包含一个对VFAT模块的引用,该引用在装入VFAT模块时添加。内核成功调用模块的初始化例程之后继续模块的安装,最后,模块状态被设置为Running(运行)。模块的清除例程保存在module数据结构中,在卸载模块时由内核调用。9.1.2模块的卸载和模块的加载类似,可利用rmmod命令手工卸载模块,当对需求加载的模块则由kerneld在不再需要时自动卸载。每次kerneld的空闲定时器到期时,它会利用系统调用将当前不再使用的需求加载模块从内核中移走。启动kerneld时指定该定时器的时间,通常的时间为180秒。如果内核的其他部分依赖于装入的模块时,该模块不能卸载。例如,如果挂装了FAT文件系统,则不能卸载已装入的FAT文件系统模块。lsmod命令的输出会显示已安装模块的使用计数,例如:Module:#pages:Usedby:msdos51vfat41(autoclean)fat62(autoclean)使用计数就是依赖于该模块的内核实体个数。模块的使用计数保存在模块映像的第一个长整型中。但是,这一长整型中还包含有AUTOCLEAN和VISITED标志。这两个标志均由需求加载的模块使用。具有AUTOCLEAN标志的模块是系统认为可以自动卸载的模块。具有VISITED标志的模块表明正由其他内核组件使用,当任何其他内核组件使用该模块是设置该标志。当kerneld请求系统移走不使用的需求加载模块时,系统首先寻找可以移走的模块,但系统只查看标记为AUTOCLEAN,并且状态处于RUNNING的模块。如果上述模块的VISITED标志被清除,则系统将卸载该模块,否则系统会清除VISITED标志并查看下一个模块。假定某个模块是可卸载的,则系统调用其清除例程释放分配该模块的内核资源。相应的module数据结构被标志为DELETED并从内核模块链表中断开。所有由该模块依赖的模块,系统会修改它们的引用表以便取消依赖性。最后,系统释放模块的内核内存。9.1.3内核模块的管理9.1.3内核模块的管理在Linux里,除了直接修改系统核心的源代码,把设备驱动程序加进核心里以外,还可以把设备驱动程序作为可加载的模块,由系统管理员动态地加载它,使之成为核心的一部分。也可以由系统管理员把已加载地模块动态地卸载下来。Linu

1 / 75
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功