模块的动态加载和系统配置.ppt
《模块的动态加载和系统配置.ppt》由会员分享,可在线阅读,更多相关《模块的动态加载和系统配置.ppt(75页珍藏版)》请在三一办公上搜索。
1、第9章 模块的动态加载和系统配置,第9章 模块的动态加载和系统配置,本章介绍了Linux内核动态加载功能模块的工作原理。分析了 Linux 内核中的系统配置结构,解释了 Makefile 和配置文件的格式以及配置语句的含义。最后给出一个简单的例子,说明如何将自行开发的代码加入到 Linux 内核中。,9.1 模块的动态加载,操作系统通常由内核和一些系统服务程序(命令解释、库文件、链接和编译程序等)组成。内核是操作系统的灵魂,它为用户进程提供了一个虚拟机接口。用户进程可以并行运行、公平的占用系统资源而互不干扰。,9.1 模块的动态加载,操作系统通常由内核和一些系统服务程序(命令解释、库文件、链接
2、和编译程序等)组成。内核是操作系统的灵魂,它为用户进程提供了一个虚拟机接口。用户进程可以并行运行、公平的占用系统资源而互不干扰。从结构上来分,可将操作系统分为微内核结构和单块结构两类。Windows NT 和 MINIX 是典型的微内核操作系统,而Linux 则是单块结构的操作系统。微内核结构可方便地在系统中添加新的组件,而单块结构却不容易做到这一点。为此,Linux系统使用可动态加载和卸载的内核模块(Loadable Kernel Modules,LKMs),可方便地在内核中添加新的组件或卸载不再需要的内核组件。Linux使用insmod来显式加载内核模块,使用rmmod来卸载模块。同时内核
3、自身也可以请求内核后台进程kerneld来加载与卸载模块。Linux模块大多数是设备驱动程序以及伪设备驱动程序模块,如网络设备和文件系统等。,动态可加载代码的优点是可以让内核保持很小的尺寸并非常灵活。模块机制可以无需重构内核并频繁重新启动来尝试运行新内核代码。用户可以根据自己系统的需要构筑自己的私有内核。Linux源码的公开更是为改造其内核、重建有特殊要求的操作系统提供了可能。,模块必须能够找到其需要使用的内核资源。例如模块需要分配内存时,要调用内核的内存分配例程kmalloc()。但在构造模块时并不知道kmalloc()在内存中何处,这样内核必须在使用这些模块前修改模块中对 kmalloc(
4、)的引用地址。内核在其内核符号表中维护着一个内核资源链表这样当加载模块时它能够解析出模块 中对内核资源的引用。Linux还允许存在模块堆栈,它在模块之间相互调用时使用。例如,因为VFAT(VirtuaI File Allocation Table)文件系统是从FAT(File Allocation Table)文件系统中扩展而来,VFAT文件系统模块可能需要FAT文件系统模块的服务。某个模块对其他模块的服务或资源的需求类似于模块对内核本身资源或服务的请求。不过此时所请求的服务是来自另外一个事先已加载的模块。当加载模块时,内核就把新近加载模块输出的所有资源和符号添加到内核符号表(Kernel-S
5、ymbol-Table)中。,在/proc/ksyms.里面的每一个表项代表着一个公共的内核符号,这就是内核符号表。这些内核符号是可以被LKM引用的。LKM中所存取的每一个符号(像函数名)也会被列在这个文件里面。在该文件中可以看到LKM到底可以调用那些函数。当试图卸载某个模块时,内核需要知道此模块是否已经没有被使用,同时它需要有种方法来通知此将卸载模块。模块必须能够在从内核种删除之前释放其分配的所有系统资源,如内核内存或中断。当模块被卸载时,内核将从内核符号表中删除所有与之对应的符号。,但是,内核模块的引入也带来了如下问题:,但是,内核模块的引入也带来了如下问题:,(1)有可能同时带来与内核模
6、块相关的性能与内存损失。可加载模块的代码一般较长,且额外的数据结构可能会占据一些内存,对内核资源的间接使用也可能带来效率方面的问题。对系统性能和内存利用有负面影响;,但是,内核模块的引入也带来了如下问题:,(1)有可能同时带来与内核模块相关的性能与内存损失。可加载模块的代码一般较长,且额外的数据结构可能会占据一些内存,对内核资源的间接使用也可能带来效率方面的问题。对系统性能和内存利用有负面影响;(2)一旦Linux模块被加载,则和普通内核代码一样成为内核的一部分,具有与其他内核代码相同的权限与职责。因此,Linux内核模块也可以象所有内核代码和设备驱动一样使内核崩溃;,但是,内核模块的引入也带
7、来了如下问题:,(1)有可能同时带来与内核模块相关的性能与内存损失。可加载模块的代码一般较长,且额外的数据结构可能会占据一些内存,对内核资源的间接使用也可能带来效率方面的问题。对系统性能和内存利用有负面影响;(2)一旦Linux模块被加载,则和普通内核代码一样成为内核的一部分,具有与其他内核代码相同的权限与职责。因此,Linux内核模块也可以象所有内核代码和设备驱动一样使内核崩溃;(3)为了内核模块访问所有内核资源,内核必须维护符号表,并在装入和卸载模块时修改这些符号表;,但是,内核模块的引入也带来了如下问题:,(1)有可能同时带来与内核模块相关的性能与内存损失。可加载模块的代码一般较长,且额
8、外的数据结构可能会占据一些内存,对内核资源的间接使用也可能带来效率方面的问题。对系统性能和内存利用有负面影响;(2)一旦Linux模块被加载,则和普通内核代码一样成为内核的一部分,具有与其他内核代码相同的权限与职责。因此,Linux内核模块也可以象所有内核代码和设备驱动一样使内核崩溃;(3)为了内核模块访问所有内核资源,内核必须维护符号表,并在装入和卸载模块时修改这些符号表;(4)有些模块要求利用其他模块的功能,因此,内核要维护模块之间的依赖性。(5)内核必须能够在卸载模块时通知模块,并且要释放分配给模块的内存和中断等资源;(6)内核版本和模块版本的不兼容,也可能导致系统崩溃,因此,严格的版本
9、检查是必需的。,模块的加载,模块的加载,有两种方法可用来加载模块:(1)利用 insmod 命令手工将模块插入内核;,模块的加载,有两种方法可用来加载模块:(1)利用 insmod 命令手工将模块插入内核;(2)由内核在必要时加载模块,称为“需求加载”。,当内核发现有必要加载某个模块时,如用户安装了内核中不存在的文件系统时,内核将请求内核后台进程(kerneld)准备加载适当的模块。这个内核后台进程仅仅是一个带有超级用户权限的普通用户进程。当系统启动时它也被启动并为内核打开了一个进程间通讯(IPC)通道。内核可以利用该通道向Kerneld进程发送任务的执行请求。Kerneld进程的主要功能是加
10、载和卸载模块,另外,该进程也负责其他一些任务,例如打开和关闭 PPP 链接等。kerneld自身并不执行这些任务,它通过某些程序如insmod来做此工作。因此,该进程实际是代表内核进行调度的代理。,执行 insmod 命令时,必须指定要加载模块的位置;对需求加载的内核模块,通常保存在/lib/modules/kernel-version。和系统的其他程序一样,内核模块实际是经连接的目标文件,但模块是可重定位的,也就是说,为了让装入的模块和已有的内核组件之间可以互相访问,模块不能连接为从特定地址执行的映像文件。模块可以是 a.out 或 elf 格式的目标文件。insmod 利用一个特权系统调用
11、,可找到内核的导出符号表,符号成对出现,一个是符号名称,另外一个是符号的值,例如符号的地址。内核维护一个由 module_list 指针指向的 module 链表,其中第一个 module 数据结构保存有内核的导出符号表(见图 9.1)。并不是所有的内核符号均在符号表中导出,而只有一些特殊的符号才被添加到符号表中。例如,“request_irq”是一个导出符号,它是一个内核例程,可由驱动程序申请控制某个特定的系统中断。利用 ksyms 命令或查看/proc/ksyms 文件内容,可非常方便地看到所有的内核导出符号及其符号值。利用 ksyms 命令,不仅可以看到内核的所有符号,也可以看到只由以加
12、载模块导出的符号。insmod 命令将模块读到它本身的虚拟内存中,然后利用内核导出的符号表,修正尚未解析的对内核例程的引用。这种修正实际是对模块在内存中的映像进行修正,insmod 将符号的地址写入模块中适当的位置而实现修正。,图 9.1 装入 VFAT 和 FAT 之后的内核模块表,insmod 命令修正模块对内核符号的引用之后,再次利用特权系统调用请求内核分配足够的物理内存空间保存新的模块。内核将分配新的 module 数据结构以及足够的内核内存,并将新模块添加在内核模块表的末尾。新的内核模块标记为Uninitialized(未初始化)。图 9.1是装入 VFAT 和 FAT 模块之后的内
13、核模块表。图中并没有表示出第一个模块,它实际是一个伪模块,仅仅用来保存内核的导出符号表。利用 lsmod 命令可列出所有已加载的内核模块以及它们的内在依赖性。内核为新模块分配的内核内存映射到 insmod 进程的地址空间中,这样,insmod 就可以将模块复制到新分配的内存中。insmod 还对模块进行重新定位,经重定位之后,新的模块就可以从新分配的内核地址开始运行了。显然,模块不能期望自己能够在不同的 Linux 系统,或前后两次装入时被加载到相同地址,重定位操作可通过对模块映像中适当的地址进行修正而解决这一问题。,新的模块也要向内核导出符号,由 insmod 建立相应的符号表。每个内核模块
14、必须包含模块的初始化和清除例程,这些例程作为每个模块均具备的例程而不被导出,但 insmod 必须知道它们的地址,并将地址传递给内核。insmod 同样利用特权系统调用将模块的初始化和清除例程地址传递给内核。新的模块添加到内核之后,它必须更新内核符号集并修改使用新模块的模块。由其他模块依赖的模块必须在自身符号表的末尾维护一个引用表,并指向其他模块的 module 结构。例如,图 9.1 表明 VFAT 文件系统模块依赖于 FAT 文件系统模块,因此,FAT 模块包含一个对 VFAT 模块的引用,该引用在装入 VFAT 模块时添加。,内核成功调用模块的初始化例程之后继续模块的安装,最后,模块状态
15、被设置为 Running(运行)。模块的清除例程保存在 module 数据结构中,在卸载模块时由内核调用。,9.1.2 模块的卸载,和模块的加载类似,可利用 rmmod 命令手工卸载模块,当对需求加载的模块则由 kerneld 在不再需要时自动卸载。每次 kerneld 的空闲定时器到期时,它会利用系统调用将当前不再使用的需求加载模块从内核中移走。启动 kerneld 时指定该定时器的时间,通常的时间为 180 秒。,如果内核的其他部分依赖于装入的模块时,该模块不能卸载。例如,如果挂装了 FAT 文件系统,则不能卸载已装入的 FAT 文件系统模块。lsmod 命令的输出会显示已安装模块的使用计
16、数,例如:Module:#pages:Used by:msdos 5 1 vfat 4 1(autoclean)fat 6 2(autoclean),使用计数就是依赖于该模块的内核实体个数。模块的使用计数保存在模块映像的第一个长整型中。但是,这一长整型中还包含有 AUTOCLEAN 和 VISITED 标志。这两个标志均由需求加载的模块使用。具有 AUTOCLEAN 标志的模块是系统认为可以自动卸载的模块。具有 VISITED 标志的模块表明正由其他内核组件使用,当任何其他内核组件使用该模块是设置该标志。当 kerneld 请求系统移走不使用的需求加载模块时,系统首先寻找可以移走的模块,但系统
17、只查看标记为 AUTOCLEAN,并且状态处于 RUNNING 的模块。如果上述模块的 VISITED 标志被清除,则系统将卸载该模块,否则系统会清除 VISITED 标志并查看下一个模块。假定某个模块是可卸载的,则系统调用其清除例程释放分配该模块的内核资源。相应的 module 数据结构被标志为 DELETED 并从内核模块链表中断开。所有由该模块依赖的模块,系统会修改它们的引用表以便取消依赖性。最后,系统释放模块的内核内存。,9.1.3 内核模块的管理,9.1.3 内核模块的管理,在Linux里,除了直接修改系统核心的源代码,把设备驱动程序加进核心里以外,还可以把设备驱动程序作为可加载的模
18、块,由系统管理员动态地加载它,使之成为核心的一部分。也可以由系统管理员把已加载地模块动态地卸载下来。Linux的模块可以用C语言编写,用gcc编译成目标文件(不进行链接,作为*.o文件存在),为此需要在gcc命令行里加上-c的参数。在编译时,还应该在gcc的命令行里加上这样的参数:-D_KERNEL_-DMODULE。由于在不链接时,gcc只允许一个输入文件,因此一个模块的所有部分都必须在一个文件里实现。,编译好的模块*.o放/lib/modules/xxxx/misc下(xxxx表示核心版本,如在核心版本为时应该为/),然后用depmod-a使此模块成为可加载模块。模块用insmod命令加载
19、,用rmmod命令来卸载,并可以用lsmod命令来查看所有已加载的模块的状态。利用 insmod 命令可手工装入内核模块;利用 lsmod 可查看当前装入的内核模块以及需求加载模块的使用计数及标志信息;利用 rmmod 则可以卸载指定的模块。,编写模块程序的时候,必须提供两个函数,一个是intinit_module(void),供insmod在加载此模块的时候自动调用,负责进行设备驱动程序的初始化工作。init_module返回0以表示初始化成功,返回负数表示失败。另一个函数是void cleanup_module(void),在模块被卸载时调用,负责进行设备驱动程序的清除工作。在成功的向系统
20、注册了设备驱动程序后(调用register_chrdev成功后),就可以用mknod命令来把设备映射为一个特别文件,其它程序使用这个设备的时候,只要对此特别文件进行操作就行了。,9.2 Linux 内核配置系统,随着 Linux 操作系统的广泛应用,特别是 Linux 在嵌入式领域的发展,越来越多的人开始投身到 Linux 内核级的开发中。面对日益庞大的 Linux 内核源代码,开发者在完成自己的内核代码后,都将面临着同样的问题,即如何将源代码融入到 Linux 内核中,增加相应的 Linux 配置选项,并最终被编译进 Linux 内核。这就需要了解 Linux 的内核配置系统。Linux 内
21、核是由分布在全球的 Linux 爱好者共同开发的,Linux 内核每天都面临着许多新的变化。但是,Linux 内核的组织并没有出现混乱的现象,反而显得非常的简洁,而且具有很好的扩展性,开发人员可以很方便的向 Linux 内核中增加新的内容。原因之一就是 Linux 采用了模块化的内核配置系统,从而保证了内核的扩展性。,9.2.1 配置系统的基本结构,9.2.1 配置系统的基本结构,Linux内核的配置系统由三个部分组成:(1)Makefile:分布在 Linux 内核源代码中的Makefile 定义 Linux 内核的编译规则;(2)配置文件(config.in):给用户提供配置选择的功能;(
22、3)配置工具:包括配置命令解释器(对配置脚本中使用的配置命令进行解释)和配置用户界面(提供基于字符界面、基于 Ncurses 图形界面以及基于 Xwindows 图形界面的用户配置界面,各自对应于 Make config、Make menuconfig 和Make xconfig)。,这些配置工具都是使用脚本语言,如 Tcl/TK、Perl 编写的(也包含一些用 C 编写的代码)。除非是配置系统的维护者,一般的内核开发者无须了解配置系统的原理,只需要知道如何使用配置系统,如何编写 Makefile 和配置文件就可以。所以,本节只对 Makefile 和配置文件进行讨论。凡是涉及到与具体 CPU
23、 体系结构相关的内容,都以 ARM 为例,这样不仅可以将讨论的问题明确化,而且对内容本身不产生影响。,9.2.2 Makefile,Makefile 的作用是根据配置的情况,构造出需要编译的源文件列表,然后分别编译,并把目标代码链接到一起,最终形成Linux 内核二进制文件。,由于 Linux 内核源代码是按照树形结构组织的,所以 Makefile 也被分布在目录树中。Linux 内核中的 Makefile 以及与 Makefile 直接相关的文件有:Makefile:顶层 Makefile,是整个内核配置、编译的总体控制文件。.config:内核配置文件,包含由用户选择的配置选项,用来存放内
24、核配置后的结果(如 make config)。arch/*/Makefile:位于各种 CPU 体系目录下的 Makefile,如 arch/arm/Makefile,是针对特定平台的 Makefile。各个子目录下的 Makefile:比如 drivers/Makefile,负责所在子目录下源代码的管理。Rules.make:规则文件,被所有的 Makefile 使用。,用户通过 make config 配置后,产生了.config。顶层 Makefile 读入.config 中的配置选择。顶层 Makefile 有两个主要的任务:产生 vmlinux 文件和内核模块(module)。为了达
25、到此目的,顶层 Makefile 递归的进入到内核的各个子目录中,分别调用位于这些子目录中的 Makefile。至于到底进入哪些子目录,取决于内核的配置。在顶层 Makefile 中,有一句:include arch/$(ARCH)/Makefile,包含了特定 CPU 体系结构下的 Makefile,这个 Makefile 中包含了平台相关的信息。位于各个子目录下的 Makefile 同样也根据.config 给出的配置信息,构造出当前配置下需要的源文件列表,并在文件的最后有 include$(TOPDIR)/Rules.make。,Rules.make 文件起着非常重要的作用,它定义了所有
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 模块 动态 加载 系统配置
链接地址:https://www.31ppt.com/p-6422344.html