什么是内核模块?
当一个操作系统发展进化到一定阶段的时候,设计者们往往要面对一种进退两难的局面。如果把对所有功能的支持都放到操作系统内核里,那么控制系统的核心程序--系统内核就会变的非常大而且笨拙。如果把其中一些功能不包含在内核之内,那么这些功能在运行的时候就会非常慢,甚至根本不能运行。操作系统的设计人员解决这种两难局面的典型方法是把这些去留两可的功能模块化。
传统来讲,模块化有两个方法解决。设计者可以把各项功能分离到单独的叫做线程的处理中去,或者是将内核以包含/排除一些功能的方式重新编译。如果把功能分离到线程中去,那么内核就叫做“微内核”(micro-kernel)这种解决方法增加了线程间为协调工作而通讯的开销。就象名字暗示的那样,这种解决方案的优点在于内核的大小。Linux的解决方案是包含内核模块,这些模块是可以按需要随时装入和卸下的。这样做可以使得内核的大小和通讯量都达到最小。
模块支持的发展
自从我开始使用linux,这个系统里就一直有内核模块。随着Linux的发展,模块的数量、支持机制和对它们的操作也一直在发展。
最初,是使用modprobe/depmod和insmod/lsmod/rmmod/ksyms来管理这些模块。这些工具在一些手册(man page)中有所描述。在Linux 1.3中,加入了一个叫kerneld的守护程序,它可以自动管理内核模块。kerneld mini-HOWTO(ftp://metalab.unc.edu/pub/Linux/docs/HOWTO/)中对它有详细的介绍。
什么时候应该使用模块?
模块一般用来支持那些仅于部分时间运行的功能。例如,通常情况下你仅使用拨号网络,因此网络功能并不是任何时候都需要的,那么就应该使用可装入的模块来提供这个功能。
仅当你拨通ISP的时候,该模块才被装入。而在你断掉连接的时候它会被自动卸下。这样会使内核使用内存的量最小,而且可以加速操作。
当然,那些象硬盘存取这样时时刻刻都需要的功能,则必须作在内核里。如果你搭一台网络工作站或web服务器,那么网络功能是时刻都需要的,你就应该考虑把网络功能编译到内核里。另外一个方法是在启动的时候就装入网络模块。这种方法的优点是你不需要重新编译内核。而缺点是网络功能不能特别高效。
如何重新编译内核
linux比其他商业操作系统的一个主要优点是它的源代码是完全开放的。你可以重新编译或修改系统的任何一部分。然而这个优点是在假设你是一个计算机程序员的前提下的。你需要了解象make和C语言这样的一些知识。如果你仅是一个计算机最终用户,那么试着重新编译内核可能是很痛苦的。此外,如果你在编译的过程中犯了错误,你可能不得不整个重新安装。就我本人而论,只有在所有的其他可能的解决方案都不行的情况下才会考虑重新编译内核。
然而,就象有人说,重新编译是非常简单的。linux的文档Kernel-HOWTO (ftp://metalab.unc.edu/pub/Linux/docs/HOWTO/)里给出了重新编译的详细介绍。如果你懒得看这个说明,那么大体的步骤是这样:先把解包,tar -zxvf linux-2.x.xx.tar.gz,源代码缺省的路径是在/usr/src/linux目录里。然后确认一下/usr/include/asm,/usr/include/linux和/usr/include/scsi目录是否正确的连接在源代码的目录里:
cd /usr/include
rm -rf asm linux scsi
ln -s /usr/src/linux/include/asm-i386 asm
ln -s /usr/src/linux/include/linux linux
ln -s /usr/src/linux/include/scsi scsi确认一下没有以前留下来的旧的.o文件:
cd /usr/src/linux
make mrproper配置内核,make config或者用带菜单的make menuconfig或Xwindow下的make xconfig来做这一步,把你所需要的功能包括进内核里,然后用make dep来设置所有的依赖关系都是正确的,下面用make zImage来编译一个压缩的内核映像,如果你装了lilo的话还要make zlilo一下来通知Lilo使用新内核来引导系统。如果你在前面make config的时候把某些功能作成模块的话,还需要make modules,然后在make modules_install。最后重新启动就可以了。
如何在启动的时候装入内核模块
要在启动的时候装入模块,必须把装入命令包括进适当的启动脚本里。Linux系统的启动脚本并不包括在任何官方发布的linux包中,每种linux发布,象Red Hat、Slackware、Debian等都有他们自己的启动脚本。因此不可能给出每种系统的更详细的介绍。读者应该自己查找一下你用的linux发布的脚本文件是哪个。首先你应该看看有没有叫''rc*.d''的目录或文件,这里星号的意思是这个位置可能是任何字符,通常启动脚本都是叫这个名字的。然后使用grep命令看找找''depmod -a''和''insmod''。linux的模块安装文档Modules Installation mini-HOWTO (ftp://metalab.unc.edu/pub/Linux/docs/linux-doc-project/)里有更详细的说明。
我必须提醒你注意,在更改这些启动脚本的之前最好先保存一下备份。这样万一你出错了,可以回到原来的状态。
Linux模块安装mini-HOWTO文档中推荐,如果你使用RedHat或Debian,最好创建一个叫''/etc/rc.d/init.d/modules.init''的文件来包含这些装入命令。然而我的RedHat版本在''/etc/rc.d/rc.sysinit''文件里有''depmod -a''命令,你可能会找到至少一行包含modprobe或insmod命令。把那些你想在启动的时候就装入的模块写在这个文档中。
重新启动计算机以后,使用lsmod命令来检查模块是否已经被激活运行了。
按需自动装入模块
自从1.3.57版本的linux内核以后,系统中就加入了一个叫做''kerneld''的守护进程(daemon),这个程序的工作是在需要的时候自动装入模块,而在不需要的时候自动卸下它。这样做的好处是仅仅需要发行内核版本的一个发布。这个daemon会按需要自动装入特定系统的特定功能模块。如果你的系统的启动脚本里包含''/sbin/kerneld''这样的一行,那你的系统里就被设置为使用这个程序。
很棒的是你所需要的所有模块都可以在不要任何额外设置的情况下正确运行。如果你需要改变设置,kerneld mini-HOWTO文档中详细描述了这些配置文件和应该如何修改它。
如何从脚本程序中装入模块
有时你需要人为地控制一个模块什么时候被装入。那么首先你需要修改一下配置文件,以确认该模块不会被kerneld自动的装入。然后在写一个脚本来启动和终止这个功能模块。
在系统启动脚本里的''depmod -a''命令会给系统中的所有可用的模块创建一个从属关系的列表。这个很必要,因为某些模块是假设其他一些模块已经被装入的。命令:''modprobe module-name''会使用这个从属列表,以使得在装入指定的模块前先装入那些事先装入的模块。如果在这个从属列表中找不到''module-name''的话,它会给出一个出错信息。如果所有依赖的其他模块都已经被装入了,那么可以使用insmod命令来装入它。
举个例子,比如说要使用拨号网络。可能有几个脚本是用来启动拨号网络的。你需要把modprobe或insmod命令加到这个脚本里去,来装入网络功能模块。我推荐使用lsmod和grep来检查一下该模块是否已经被装入了。你还需要在终止拨号网络的的脚本里加一行rmmod命令来卸下网络功能模块。关于拨号网络脚本的细节,请看PPP mini-HOWTO (ftp://metalab.unc.edu/pub/Linux/docs/linux-doc-project/)。
更多资料
我收集的很多关于模块的资料都是来自linux文档工程LDP。这里是该工程的主页http://metalab.unc.edu/mdw/ldp.html。
Linux文档工程(LDP)是专门开发关于linux系统的好的可靠的文档资源的。LDP的目标是收集和整理各种在线文档,包括手册man page,html等,内容覆盖linux的安装、使用和运行等。
该工程所有已整理好的文档都可以在这里找到ftp://sunsite.unc.edu/pub/Linux/docs/。我使用的几本手册是The Linux Users'' Guide, The Linux Kernel HOWTO, The Linux Modules Installation mini- HOWTO和The kerneld mini- HOWTO.带有最好的除错信息的文档是Linux PCMCIA HOWTO,另外还有些资料来自Linux PPP HOWTO和Linux IPX HOWTO。
另外一个非常好的信息来源是Deja-News的linux的新闻组文档。这是一个把所有在linux新闻组贴过的有用的信息都收集下来的文档库。它支持全文搜索,也允许你贴新的问题上去。由于这个站的资料太多,因此找你需要的信息的时候要有耐心和恒心。坚持,你要找的东西通常就藏在里面。