Linux是单内核结构,也就是说, 他是个大程式, 其中任一函数都能够访问公共数据结构和其他函数调用。 (作为操作系统)另外一种可能的结构是多核式的, 各功能块自成一体, 相互之间由严格的通信机制相连。单核结构在添加新模块时,一种方法是重新调整配置,所以很费时。 比如,您想在内核中加一个NCR 810 SCSI的驱动程式, 您必须重新配置, 重建内核。这也有另外一个办法,Linux 允许动态装载和卸掉模块。Linux 模块是一段能够在机器起动后任意时间被动态连接的代码。 在无需时, 他们能够被从内核中卸掉。 大多数Linux 模块是设备驱动程式或伪设备驱动程式, 如网络驱动程式, 文档系统等。
您能够使用 insmod 和 rmmod 命令来装载和卸掉 Linux 模块, 内核自己也能够调用内核驻留程式(Kerneld) 来按需要装载和卸掉模块。
按需动态装载模块能够使内核保持最小, 并更具灵活性。 我现在的 Intel 内核由于大量使用动态装载模块, 只有 406 K 字节。例如, 我很少用到 VFAT 文档系统, 所以我让 Linux 内核只在我装载 VFAT 分区时, 才自动上载 VFAT 文档系统。 当我卸掉 VFAT 分区时, 内核会检测到, 并自动卸掉 VFAT 文档系统。 当测试新程式时, 您假如不想每次都重建内核, 动态装载模块是很有用的。 但是, 运用模块会多消耗一些内存, 并对速度有一定影响。并且模块装载程式是一段代码, 他的数据将占用一部份内存。这样还会造成不能直接访问内核资源, 效率不高的问题。
一旦 Linux 模块被装载后, 他就和一般内核代码相同, 对其他内核代码, 享受同样的访问权限。换句话说, Linux 内核模块能够像其他内核代码, 或驱动程式相同使系统崩溃。模块能够使用内核资源,但首先他需知道怎样调用。例如, 一个模块要调用 Kmalloc() (内核内存分配程式)。但在模块建立时, 他并不知道到哪儿去找 Kmalloc(), 所以在他被装载时, 内核必须先设定模块中任何 Kmalloc() 调用的函数指针。 内核有一张任何资源调用的列表, 在模块被装载时, 内核重设任何资源调用的函数指针。Linux 允许栈式模块, 即一个模块调用另一个模块的函数。例如, 由于 VFAT 文档系统能够看成是 FAT 文档系统的超集, 所以 VFAT 文档系统模块需要调用 FAT 文档系统提供的服务。一个模块调用另一模块的资源和调用内核资源很相似。唯一的不同是被调用的模块需被先载入。一个模块被载入后,内核将修改他的内核符号表(KERAEL SYMOBOL TABLE),加入新载入模块提 供的任何资源和符号。所以另一个模块被载入时, 他就能够调用任何已载入模块提供的服务。
当卸掉一模块时,内核先确定该模块不会再被调用,然后通过某种方式通知他。在该模块被内核卸掉以前,该模块须释放任何占用的系统资源。例如,内存或中断,当模块被卸掉后,内核从内核符号表中删除任何该模块提供的资源。
假如模块代码不严谨,他将使整个操作系统崩溃。另一个问题,假如您载入的是为其他版本服务的模块,那怎么办?例如,一个模块凋用一个内函数,但提供了错误的输入参数,这将导致运行错误。但内核能够在模块被载入时选择性地通过严格版本检查来杜绝这种现象。
载入模块有两种方法。第一种是通过INSTALL 命令来载入;另一种更聪明的方法是在模块被调用时自动载入,这叫所需载入(DEMAND LOADING)。例如,当用户在装一个不在内核中的文档系统,内核会自动调用内核驻 留程式(KERNELD)来载入对应的处理模块。
内核驻留程式是个具备终极用户极限的普通用户程式。当他被启动时(通常在系统启动时),他将打开一个和内核之间的进程间通信管道(IPC CHANNEL)。 内核将利用这条管道来通知进程驻留程式去完成各种任务。内核驻留程式的主要任务是载入和卸掉模块,他也能完成其她一些任务。如按需打开和关掉一条通过串口的 DDD LINK。KERNELD 自己并不完成这些任务。他将调用如INSMOD 这样的命令来完成,KERNELD 只是个内核的代理,协调完成各项任务。
载入模块时,INSMODE 命令必须先找到要被载入的模块。可所需载入的模块通常被放在/LIB/MODULES/KERNEL-VERSION下,这些模块和一般系统程式都是已连接好的目标代码,不同处在于模块是可重定位的映像文档。也就是说,模块并不是从一个固定的地址开始执行的。模块能够是 a。out, 也能够是ELF格式的目标代码。INSMODE 通过一个有系统权限的调用来找到内核中可被调用的资源。
系统(资源)符号由名和值俩部份组成。内核用MODULE_LIST 指针指向其管理的任何模块所串成的链表。内核的输出符号表在第一个MODULE 数据结构中,并不是内核任何的符号都能被模块调用,可调用符号必须被加入输出符号表中,而输出符号表是和内核一起编译连接的。例如,当一驱动程式想控制某一系统中断时,她需调用”REQUEST_IRQ”这样一个系统函数,在我机器的内核中,他现在的值是0x0010cd30,您能够看/PROC/KSYMS文档或用KSYMS 来查询。KSYMS 命令能够显示任何内核输出符号的值,也能够显示载入模块的输出符号的值。当INSMOD 载入模块时,他先将模块载入虚存,根据内核输出符号,重设任何内核资源函数调用的指针。即在模块的函数调用处写入对应符号的物理地址。
当INSMOD 重设完内核输出符号的地址后,他将调用一个系统函数,要 求内核分配足够的空间。内存就会分配一个新的MODULE 数据结构和足够的内存来装载这个新模块,并把这个MODULE 数据结构放在模块链表的最后, 置成未初使化(UNINITALIZED)。
显示的是内核载入FAT 和VFAT 两模块后的模块链表。链表的第一模块并没有显示出来,那是个伪模块,只是用来记录内核的输出符号表。您能够用ISMOD命令来列出任何载入模块及他们之间的关系。ISMOD只是格式化的输出记录内核链表的/PROC/MODULES文档。INSMOD 能够访问内核分配给新载入模块的内存,他先将模块写入这块内存,然后对他进行重定位处理,使模块能够从这个地址开始执行。由于每次模块被载入时,无论在不在同一台机器上,都不大可能分配到相同的内存地址,所以重定位(即重设他的函数指针)是必须的。
文章整理:西部数码--专业提供域名注册、虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!




