当前位置:Linux教程 - Linux资讯 - Linux内核模块使用指南

Linux内核模块使用指南

作者:sss    一、模块简介     Windows NT是一种微内核的结构,其内核的功能块被划分成独立的模块,在这些功能块之间有严格的通信机制;而Linux则不同,它是一种monolithic(单一大块)结构,也就是说,整个内核是一个单独的、非常大的程序。在这种结构中,部件的添加和删除都相当麻烦,需要重新编译内核。为了解决这个问题,不知道从哪个版本的内核开始,Linux引入了一种称为module(模块)的技术,可以把某些功能代码作为模块动态装载到内核中使用。     模块是一种目标对象文件,需要在内核空间执行,可以把它看作是一组已经编译好而且已经链接成可执行文件的程序。在需要的时候,内核就会实用某种方法调用这些程序来执行特定的操作,实现特定的功能。内核在内核符号表中维护了一个模块的链表,每个符号表对应一个模块,在把模块加载进内核时正确地对其进行解释,并将模块作为内核的一部分来执行;加载进内核中的模块具有所有的内核权限。模块可以在系统启动时加载到系统中,也可以在系统运行的任何时刻加载;在不需要时,可以将模块动态卸载。这样就不用每次修改系统的配置时都要重新编译内核了。     二、模块的优缺点     内核模块的这种动态装载特性具有以下的优点:   1、可以把内核映像文件保持在最小。在编译内核时可以选择把一部分内容当成模块进行编译,这样在最终生成的内核映像文件中就可以不包含这部分内容,从而生成最小的内核映像文件。   2、灵活性好。如果需要实用新的模块,不必重新编译内核,只要把新的模块编译后装载进系统中就可以了。如果对内核源程序进行了修改,也不需要重新编译整个内核,只需要修改对应的部分就可以了。     但是,内核模块的引入也带来了一些问题:   1、这种动态加载的特性不利于系统的性能和内存的利用,会带来负面的影响。   2、装入内核的模块和其他内核部分一样具有最高的权限,使用不当就可能引起系统的崩溃。   3、内核版本和模块版本的不兼容也会导致系统的崩溃,因此必须进行严格的版本检查,这样就使模块的编写变得更加复杂了。   4、有些模块要使用其他模块(例如VFAT就要使用FAT)的内容,模块之间存在一定的依赖关系,这样模块的实用就复杂化了。     由于模块的这种动态装载/卸载的特性,在Linux中大部分设备驱动程序都是使用模块来编写的,例如文件系统(minix、msdos、isofs、smbms、nfs、proc等等)、SCSI设备驱动程序、以太网驱动程序、CD-ROM驱动程序等等。下面让我们介绍一下模块的使用方法。     三、模块的使用     1、模块的查询     我们可以使用lsmod命令来了解系统中现在装载进来了哪些模块。例如,在笔者机器上执行的结果为(注意,以下介绍的这些命令(包括lsmod)只有超级用户才可以执行):     Module Size Used by   lockd 30344 1 (autoclean)   sunrpc 52132 1 (autoclean) [lockd]   rtl8139 11748 1 (autoclean)     其中Module列是模块的名字,Size是显示的模块的大小,Used by列表示引用次数,圆括号中的autoclean表示该模块可以在空闲时自动卸载,中括号中的[lockd]表示模块lockd会引用sunrpc模块的内容。       2、模块的装载     模块的装载有两种方法:一种是实用insmod命令手工加载模块,第二种方法是使用内核守护进程kerneld在需要的时候动态装载。insmod命令的格式为:     insmod /

/modulename.o     值得注意的是,insmod命令需要知道模块存放的位置,这样才能在内核符号表中进行解析。模块可以位于当前路径中,也可以在insmod命令中指明绝对路径,另外还有几个相关的配置文件可以说明模块的位置(见后文中的介绍)。     kerneld是一个标准的守护进程,具有超级用户的权限,其主要功能是加载和卸载核心模块, 但是它还可以执行其他任务, 如通过串行线路建立PPP连接并在适当时候关闭它。kerneld自身并不执行这些任务,它通过某些程序如insmod来做此工作。它只是内核的代理,为内核进行调度。这个守护进程仅仅是一个带有超级用户权限的普通用户进程。当系统启动时它也被启动并为内核打开了一个进程间通讯(IPC)通道,内核需要执行各种任务时就实用这个IPC来向kerneld发送消息。例如,如果内核请求现在还没有装载到系统中的文件系统,那么就通知kerneld装载这个文件系统,然后内核就可以使用这个文件系统了。在模块空闲时(即没有其他进程使用这个模块时),kerneld还可以动态卸载这个模块。     需要注意的是,如果模块之间有某种引用关系,那么装载模块时必须遵循一定的次序。例如,上面lsmod显示的结果中lockd模块要引用sunrpc的内容,那么必须首先装载sunrpc之后才能装载lockd,否则就会出错。       3、模块的卸载     我们可以使用rmmod命令把模块从系统中卸载出去,该命令的格式为:     rmmod modulename     需要注意的是,模块只有在空闲时才能够从系统中卸载出去。lsmod输出结果中的Used by一列就说明了模块当前的状态。如果该值不为0,说明模块正忙,不能卸载;否则该值为0,说明模块空闲,可以从系统中卸载出去。对于繁忙的设备,我们首先得断开对应设备的连接,然后才能删除对应的模块。例如,我们要卸载模块rtl8139(这个模块是笔者机器中rtl8139的以太网卡对应的模块),我们首先要断开网络连接:     ifdown eth0 /* eth0是笔者机器中的第一块网卡*/     现在再执行lsmod命令的输出结果为:     Module Size Used by   lockd 30344 1 (autoclean)   sunrpc 52132 1 (autoclean) [lockd]   rtl8139 11748 0 (autoclean)     说明已经没有设备再使用rtl8139了,我们可以使用     rmmod rtl8139     命令将其从系统中卸载出去。     标志为autoclean(见有关lsmod的介绍)的模块可以自动卸载。前面我们已经提到,模块之间可能会有引用关系。如果A模块引用了B模块的内容,那么必须先装载B模块之后才能成功装载A模块;在卸载B模块之前也要首先卸载A模块,否则就会导致系统的崩溃(当然,如果模块源程序编写的正确,在卸载A模块之前,B模块是无法卸载的)。     4、模块实用工具     以上我们介绍的lsmod、insmod、rmmod是一组实用工具所提供的三个命令,这组实用工具一般是和内核版本对应的,其1.3.57版本名为modules(modules-1.3.57.tar.gz),高一点的版本名为modutils(例如modutils-2.4.2.tar.gz)。最好保证你的系统中的模块实用工具的版本号(可以使用modinfo -V命令来查看)不低于内核版本号(可以使用uname -r来查看)。1.3.57版本的modules内容包括modprobe、depmod、genksyms、makecrc32、insmod、rmmod、lsmod、ksyms、kerneld等命令。其中modprobe和insmod命令类似,不过它要依赖于相关的配置文件;depmod用于生成模块依赖文件/lib/modules/kernel-version/modules.dep;genksyms和ksyms与内核函数的版本号有关(由于内核的不断更新,各个版本的内核函数各有不同,为了不会引起系统的崩溃,内核源程序中要对内核函数的版本号进行严格地控制)。在以后版本的实用工具中,使用kmod来取代了kerneld。kmod的功能和kerneld类似,但是它不能自动卸载模块。之所以采用kmod的原因在于kerneld是使用IPC通道实现的,相当于多经过了一层处理,另外kerneld的代码也比较复杂,kmod的代码数量也比kerneld少得多。       5、与模块有关的内核编译选项和过程     在使用make confing / make menUConfig / make xconfig对内核进行配置时,和模块有关的选项有:   Code maturity level options -->   Prompt for development and/or incomplete code/drivers   此选项为代码的成熟程度。所有新的设计与改进都首先在开发版(版本号为x.y.z,其中y是奇数)中试用,经过验证技术可行之后再在稳定版(版本号为x.y.z,其中y是偶数)中正式引入。尚不成熟或不完善的技术在默认的情况中是不会编译到内核中的,如果你希望试验这些内容(例如2.4.*版本中的khttpd、IPV6等),就要选中这个选项。   >   Loadable module support -->   Enable module support   Set version information on all module symbols   Kernel module loader   此选项是对可装载内核的支持以及对模块符号的版本号、内核模块装载程序支持的选项。对于其他大部分选项来说,你可以将相应的代码编译到内核中(使用build-in方式),也可以将他们编译成模块(使用module)方式。     如果你选用了模块方式,那么在编译内核的过程中,你除了要使用     make; make install     命令来编译/安装内核之外,还要使用     make modules; make modules_install     来编译/安装内核模块。编译好的模块被安装到/lib/modules/kernel-version/目录中。     编译过程中还要运行一个     depmod -a     命令。这个命令生成模块依赖文件,也就是/lib/modules/kernel-version/modules.dep,该文件格式为:     /lib/modules/2.2.14-5.0/fs/autofs.o:     /lib/modules/2.2.14-5.0/fs/binfmt_aout.o:     /lib/modules/2.2.14-5.0/fs/binfmt_Java.o:     /lib/modules/2.2.14-5.0/fs/binfmt_misc.o:     /lib/modules/2.2.14-5.0/fs/coda.o:     /lib/modules/2.2.14-5.0/fs/fat.o:     /lib/modules/2.2.14-5.0/fs/hfs.o:     /lib/modules/2.2.14-5.0/fs/hpfs.o:     /lib/modules/2.2.14-5.0/fs/lockd.o: /lib/modules/2.2.14-5.0/misc/sunrpc.o     /lib/modules/2.2.14-5.0/fs/minix.o:     /lib/modules/2.2.14-5.0/fs/msdos.o: /lib/modules/2.2.14-5.0/fs/fat.o     ......     这个文件中详细列出了所有的模块文件的绝对路径,每一行说明一个模块的信息;如果一行中有多个模块的信息,就说明了模块间具有依赖关系。例如,     /lib/modules/2.2.14-5.0/fs/lockd.o: /lib/modules/2.2.14-5.0/misc/sunrpc.o   这一行就说明模块lockd要引用sunrpc模块。有了这个文件,内核在装载模块时就可以根据所解析出的模块名来正确地定位模块的位置了。       6、与模块有关的配置文件     和模块有关的文件,除了上面这个modules.dep之外,还有几个配置文件。例如:     /etc/rc.d/rc.modules   /etc/conf.modules或/etc/modules.conf     例如笔者机器中后一个文件的内容为:     alias eth0 rtl8139   alias sound-slot-0 via82cxxx     分别说明了网卡和声卡的类型,例如第一行中eth0是我的第一块网卡,rtl8139是对应的模块,系统会查找/lib/modules/2.2.14-50/modules.dep文件,在其中找到     /lib/modules/2.2.14-5.0/net/rtl8139.o:     这一行,从而就可以定位正确的模块文件。     7、单独编译模块     我们除了可以在内核源程序树中使用     make modules; make modules_install     来编译安装内核模块之外,也可以自行对其进行编译,相关的编译选项有:     __KERNEL__--这个标志告诉头文件此代码将在内核模块中运行,而不是作为用户进程的一部分运行。     MODULE--这个标志告诉头文件要为内核模块给出适当的定义。     LINUX--从技术上讲,这个标志不是必要的。但是,如果你希望写一个比较正规的内核模块,它可以在多个操作系统上编译,这个标志将会使你感到非常方便。它可以允许你在独立于操作系统的部分进行常规的编译。     还有其它一些可被选择包含标志,这取决于编译模块使用的选项。如果你不能确定内核应该怎样编译,可以在/usr/include/linux/config.h中查到。     __SMP__--对称多处理。如果要把内核编译成可以支持对称多处理,那么必须定义这个标志     CONFIG_MODVERSIONS--如果CONFIG_MODVERSIONS被激活(也就是说要区分内核模块的版本号),你需要在编译内核模块时定义这个标志并包含头文件/usr/include/linux/modversions.h。     例如,我们自行下载rtl8139网卡的驱动程序的源程序rtl8139.c,可以这样编译:     gcc -Wall -O2 -DMODULE -D__KERNEL__ -DLINUX -o rtl8139.o rtl8139.c     然后修改前面介绍的相应的配置文件或者使用insmod命令就可以使用这个模块了。       8、相关问题     再次强调,模块实用工具的版本号最好不要低于内核的版本号。例如,如果你的modutils的版本号小于2.4.0,那么在你自己下载2.4.*版本的内核来编译时,即使各个步骤都完全正确,你会发现/lib/modules/kernel-version/modules.dep这个文件为空,即使你自己手工修改了这个文件,重新启动机器之后,这个文件又变成空了。所以对于这种情况,你应该使用2.4.*的modutils。2.2和2.4版本的内核模块存储的路径有很大的差别,/etc/conf.modules文件的结构也很不相同了,感兴趣的读者可以自行试验一下。   


[1] [2] 下一页 

 

 

(出处:http://www.sheup.com)


上一页 [1] [2]