📄 (ldd) ch11-kerneld和高级模块化(转载).htm
字号:
color=#ffffff size=3>
<P>自动启动。<BR> <BR> <BR> <BR>用于合并符号名字的主要工具是头文件<linux/modversions.h>,它包括了所有公共内核<BR>符号的预处理定义。在包含这个头文件后,不管模块何时使用了内核符号,编译器都将<BR>看到合并了的版本。modversions.h中的定义只有预先定义过MODVERSIONS才有效。<BR> <BR> <BR> <BR>如果内核已经启动了版本支持,为了在模块中也启动它,我们必须保证在<linux/autoco<BR>nf.h>中已经定义过CONFIG_MODVERSIONS。那个头文件控制着在当前内核中(编译时)启动<BR>了哪些特性。每个CONFIG_宏定义声明相应选项的状态是否要激活。<BR> <BR> <BR> <BR>这样,master.c的初始化部分包含如下部分:<BR> <BR> <BR> <BR>#include <linux/autoconf.h> /* 检索CONFIG_*宏 */<BR> <BR>#if defined(CONFIG_MODVERSION) && !defined(MODVERSIONS)<BR></P></FONT><FONT
color=#ffffff size=3>
<P>#if defined(CONFIG_MODVERSION) && !defined(MODVERSIONS)<BR> <BR># define MODVERSIONS /* 强迫打开它 */<BR> <BR>#endif<BR> <BR> <BR> <BR>#ifdef MODVERSIONS<BR> <BR># include <linux/modversions.h><BR> <BR>#endif<BR> <BR> <BR> <BR>在版本化的内核上编译这个文件时,目标文件的符号表会引用版本化符号,这些版本化<BR>符号匹配内核本身开放的那些符号。下面的屏幕快照显示了master.o中储存的符号名字<BR>。在nm的输出中,"T"代表“文本(text)”,"D"代表“数据(data)”,"U"代表“未定义<BR>(undefined)”。最后一个标记表示目标文件引用了但没有声明的符号。<BR> <BR> <BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>morgana% nm master.o<BR> <BR>000000b0 T cleanup_module<BR> <BR>00000000 T init_module<BR> <BR>00000000 D kernel_version<BR> <BR>U kerneld_send_R7d428f45<BR> <BR>U printk_Rad1148ba<BR> <BR>morgana% egrep 'printk|kerneld_send' /proc/ksyms<BR> <BR>00131b40 kerneld_send_R7d428f45<BR> <BR>0011234c printk_Rad1148ba<BR> <BR> <BR> <BR>因为加到master.o中符号名上的校验和包含了与printk和kerneld_send相关的整个接口<BR>,模块与大部分内核版本都兼容。然而,如果与其中任一函数有关的数据结构被改变了<BR></P></FONT><FONT
color=#ffffff size=3>
<P>,模块与大部分内核版本都兼容。然而,如果与其中任一函数有关的数据结构被改变了<BR>,insmod将因为模块与内核的不兼容而拒绝装载它。<BR> <BR>开放版本化符号<BR>以前的讨论中未涉及的情况是,当其它模块使用一个模块开放的符号时,会发生写什么<BR>。如果依赖版本信息来获得模块的可移植性,那么我们希望能把CRC校验码加到我们自己<BR>的符号上去。这个问题比仅仅链接到内核技巧性更高一些,因为我们需要将合并后的符<BR>号名向其它模块开放;我们需要一种办法来生成校验和。<BR> <BR> <BR> <BR>分析头文件和生成校验和的任务是由随modules包一起发行的一个工具genksyms来做的。<BR>这个程序在自身的标准输入上接受C预处理器的输出,并在标准输出上打印出一个新的头<BR>文件。这个输出文件定义了原来那个源文件开放出来的每个符号的带检验和的版本。gen<BR>ksyms的输出通常以后缀.ver保存;下面我将遵循同样的惯例。<BR> <BR> <BR> <BR>为了显示如何开放符号,我生成了两个名为export.c和import.c的虚构的模块。export<BR>开放了一个名为export_function的简单函数,并且该函数会被第二个模块import.c使用<BR>。这个函数接收两个整数参数并返回它们的和--我们对这个函数并不感兴趣,而是对链<BR>接过程更感兴趣。<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR> <BR> <BR>misc-modules目录下的Makefile文件有从export.c生成export.ver文件的规则,因此exp<BR>ort_function的检验和符号可以被import模块使用:<BR> <BR> <BR> <BR>ifdef MODVERSIONS<BR> <BR>export.o import.o: export.ver<BR> <BR>endif<BR> <BR> <BR> <BR>export.ver: export.c<BR> <BR> $(CC) -I$(INCLUDEDIR) -E -D__GENKSYMS__ $^|genksyms > $@<BR> <BR> <BR> <BR>这几行演示了如何生成export.ver,并且只有定义过了MODVERSIONS才会把它加到两个目<BR></P></FONT><FONT
color=#ffffff size=3>
<P>这几行演示了如何生成export.ver,并且只有定义过了MODVERSIONS才会把它加到两个目<BR>标文件的依赖关系中去。如果内核启动了版本支持,还要添加几行到Makefile中负责定<BR>义MODVERSIONS,但并不值得在这里展示它们。<BR> <BR> <BR> <BR>然后,源文件必须为每个可能的预处理流程声明正确的预处理符号:不论是给genksyms<BR>的输入和真正编译过程,不论是启动还是关闭了版本支持。进一步,export.c应当能够<BR>象master.c那样自动检测内核中的版本支持。下面几行向你显示了如何成功地做到这一<BR>点:<BR> <BR> <BR> <BR>#ifndef EXPORT_SYMTAB<BR> <BR># define EXPORT_SYMTAB /* 需要这个定义是因为我们要开放符号*/<BR> <BR>#endif<BR> <BR> <BR> <BR>#include <linux/autoconf.h> /* 检索CONFIG_* 宏 */<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>#if defined(CONFIG_MODVERSIONS)&& !defined(MODVERSIONS)<BR> <BR># define MODVERSIONS<BR> <BR>#endif<BR> <BR> <BR> <BR>/*<BR> <BR> * 将内核符号和我们的符号的版本化定义包含进来,*除非*我们正在<BR> <BR> * 生成校验和(定义了*__GENKSYMS__)<BR> <BR> */<BR> <BR>#if defined(MODVERSIONS) && !defined(__GENKSYMS__)<BR> <BR># include <linux/modversions.h><BR> <BR># include "export.ver" /* 为了包含CRC,重定义了<BR>"export_function" */<BR></P></FONT><FONT
color=#ffffff size=3>
<P>"export_function" */<BR> <BR>#endif<BR> <BR> <BR> <BR>这些代码,虽然令人讨厌,但好处是:可以让Makefile处于一个干净的状态。另一方面<BR>,由make来传递正确的标志,涉及到为各种情况编写冗长的命令行,因此我就不在这里<BR>做了。<BR> <BR> <BR> <BR>简单的import模块通过传递数字2和2作为参数,来调用export_function;期望的结果是<BR>4。下面的例子显示import确实链接到了export的版本化符号,并且调用了函数。版本化<BR>符号出现在/proc/ksyms文件中。<BR> <BR> <BR> <BR>morgana.root# insmod export<BR> <BR>morgana.root# grep export /proc/ksyms<BR> <BR>0202d024 export_function_R2eb14c1e (export)<BR></P></FONT><FONT
color=#ffffff size=3>
<P>0202d024 export_function_R2eb14c1e (export)<BR> <BR>morgana.root# insmod import<BR> <BR>import my mate tells that 2+2 = 4<BR> <BR>morgana.root# cat /proc/modules<BR> <BR>import 1 0<BR> <BR>export 3 [import] 0<BR> <BR>跨过卸载/装载的持久存储<BR>一旦我们装备上了kerneld和版本支持,就会发现使用模块比使用链进内核的驱动程序更<BR>方便。模块化只有一个问题:如果一个驱动程序由kerneld载入,然后被配置(通过ioct<BR>l或者其它方法),那么下次将该驱动程序载入内核时又必须重新配置它。而启动时的配<BR>置信息则可以在/etc/modules.conf文件中一劳永逸的指定,因此当要多次使用按需装载<BR>时,运行时的配置变得容易丧失。用户会可能会失望地发现刚离开休息一会设备的配置<BR>信息就已经丢失了。我们需要的是一种可以在模块卸载后持久地保存相关信息的技术。<BR> <BR> <BR> <BR>实际上,modules包从2.0.0版开始(modules-2.2.0)提供这种能力。<BR></P></FONT><FONT
color=#ffffff size=3>
<P>实际上,modules包从2.0.0版开始(modules-2.2.0)提供这种能力。<BR> <BR> <BR> <BR> 真正的代码还没有集成进官方的内核,但很可能会被Linus的源码所接受。目前<BR>,为了启动对持久存储的支持,你需要使用modules包中发布的一个补丁;这个补丁在<l<BR>inux/kerneld.h>中添加了几行代码。<BR> <BR> <BR> <BR>实际上,隐藏在模块信息的持久存储之后的想法很直接:与用户空间相互传输信息,内<BR>核代码可以与转载 托对啬 块使用同一个kerneld引擎。然后,守护进程使用一个通用数<BR>据库来管理信息存储。<BR> <BR> <BR> <BR>在用户空间而不是在内核空间实现持久存储的原因是为了简化代码。尽管可以设计出仅<BR>与内核空间有关的实现,从内核空间访问一个数据库文件需要将库代码在不可交换的内<BR>核空间中复制,而在用户空间,库代码的使用则没有任何开销。<BR> <BR> <BR> <BR>Kerneld中提议的实现使用了gdbm库来实现数据库。也可以选择使用盘上数据库。如果使<BR></P></FONT><FONT
color=#ffffff size=3>
<P>Kerneld中提议的实现使用了gdbm库来实现数据库。也可以选择使用盘上数据库。如果使<BR>用了该数据库,就可以获得跨过系统启动的持久性存储;如果没有使用该数据库,你只<BR>能在kerneld进程的生存期内获得持久性。<BR> <BR> <BR> <BR>下面这些函数是在头文件<linux/kerneld.h>中定义的,用于获得持久存储特性:<BR> <BR> <BR> <BR> int set_persist(char *key, void *value, size_t length);<BR> <BR> int get_persist(char *key, void *value, size_t length);<BR> <BR> <BR> <BR>这些函数的参数是一个文本关键字(key)和数据项本身-该关键字唯一地标记数据库中<BR>的一个数据项,而数据项则呈现为一个指针和长度的熟悉形式。参数key在整个系统内都<BR>必须唯一。这样就允许每个模块通过在关键字前加上模块名字将自己的关键字分离出来<BR>,但是它也允许不同的模块共享配置变量,如果因为什么原因需要这么做的话。<BR> <BR> <BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>可能的返回值和其它调用kerneld-_send的函数是一样的:0表示成功,负数通知一个内<BR>核空间的错误,而正数用于通知一个用户空间的错误。通常可以忽略返回值,因为如果<BR>有错的话,get_persist不会修改value的值,而如果set_persist不能保存这个值的话,<BR>也不会做任何事情。<BR> <BR> <BR> <BR>新近的kerneld守护进程开始支持这种新特性,所以模块也可以选择在内核中不对kernel<BR>d.h做修补而将set_persist和get_persist的定义包含进来。但要注意向前兼容。建议使<BR>用在modules中发布的补丁;在被官方的内核源代码中包含前持久存储的内部实现可能会<BR>有变化。<BR> <BR> <BR> <BR>我们已经看到,使用持久存储的主要原因是避免每次把模块载入一个运行内核时又要重<BR>新配置它。这对按需装载的模块的运行时配置尤其重要;这对装载时配置也是一个有意<BR>义的选项,因为更新/etc/modules.conf对普通用户来说有些复杂。<BR> <BR> <BR> <BR>持久存储另一个可能的用处是跟踪系统的硬件配置以避免不必要的探测。探测硬件是一<BR>种冒险的操作。它可能会错误地配置了其它的硬件,特别是对ISA设备,因为ISA不象PCI<BR></P></FONT><FONT
color=#ffffff size=3>
<P>种冒险的操作。它可能会错误地配置了其它的硬件,特别是对ISA设备,因为ISA不象PCI<BR>那样提供了一种通用的方法来扫描系统总线。(第15章“外设总线概貌”详细讨论了该<BR>问题。)<BR> <BR> <BR> <BR>下面的例子代码显示了一个名为psm(Persistent Storage Module)的假想模块是如何<BR>避免不必要的探测的。为简化讨论,这个例子程序最多支持一个设备。<BR> <BR> <BR> <BR>int psm_base = 0; /* 基本的I/O端口,在装载时可以设定 */<BR> <BR> <BR> <BR>int init_module(void)<BR> <BR>{<BR> <BR> if (psm_base==0){ /* 在装载时没有设定 */<BR> <BR> get_persist("psm_base", &psm_base, sizeof(int));<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR> if (psm_check_hw(psm_base)!=0)<BR> <BR> psm_base=0; /* 旧的数值不再有效:探测 */<BR> <BR> }<BR> <BR> else<BR> <BR> if (psm_check_hw(psm_base)!=0)<BR> <BR> return -ENODEV; /* 没有任何地方指明基地址 */<BR> <BR> <BR> <BR> if (psm_base==0)<BR> <BR> psm_base=psm_probe(); /* 返回基本端口,或者,如果没能找<BR>到就返回0 */<BR> <BR> if (psm_base==0)<BR> <BR> return -ENODEV;/* 没有找到任何设备 */<BR></P></FONT><FONT
color=#ffffff size=3>
<P> return -ENODEV;/* 没有找到任何设备 */<BR> <BR> <BR> <BR> set_persist("psm_base", &psm_base, sizeof(int));/* 找到:保存它 */<BR> <BR>}<BR> <BR> <BR> <BR>只有在装载时没有指定基本端口,并且以前的端口不再有效的时候,这些代码才探测硬<BR>件。如果找到一个设备,基本端口被保存起来,留作后用。<BR> <BR> <BR> <BR>当驱动程序要支持多个设备时,检测新增加的硬件这个问题的一个可能的办法就是定义<BR>一个psm_newhw变量,如果添加了新设备到系统,那么用户可以在装载时对该变量进行设<BR>置。如果这样实现的话,那么当存在新设备时,用户必须使用insmod psm_newhw=1命令<BR>。如果psm_newhw不为0,init_module试着探测新设备,而在通常情况下它使用的是保存<BR>的信息。一个设备的基地址中的改变在上面给出的代码中已经处理过了,而不需要用户<BR>在装载时进行干预。<BR> <BR>快速参考<BR></P></FONT><FONT
color=#ffffff size=3>
<P>快速参考<BR> 本章引入下面一些内核符号:<BR> <BR> <BR> <BR>/etc/modules.conf<BR> <BR>modprobe和depmod程序的配置文件。它用于配置按需加载,在这两个程序的man页中有描<BR>述。<BR> <BR> <BR> <BR>#include <linux/kerneld.h><BR> <BR>int request_module(const char *name);<BR> <BR>int release_module(const char *name, int waitflag);<BR> <BR>int delayed_release_module(const char *name);<BR> <BR>int cancel_release_module(const char *name);<BR> <BR> 这些函数通过kerneld守护进程进行模块的按需加载。<BR></P></FONT><FONT
color=#ffffff size=3>
<P> 这些函数通过kerneld守护进程进行模块的按需加载。<BR> <BR> <BR> <BR>#include <linux/autoconf.h><BR> <BR>CONFIG_MODVERSIONS<BR> <BR> 只有当前内核被编译成支持版本化符号时这个宏才会被定义。<BR> <BR> <BR> <BR>#ifdef MODVERSIONS<BR> <BR>#include <linux/modversions.h><BR> <BR>这个头文件只有在CONFIG_MODVERSIONS有效时才存在,它包含了内核开放的所有符号的<BR>版本化名字。<BR> <BR> <BR> <BR>EXPORT_SYMTAB<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>如果使用了版本支持并且你的模块使用了register_symtab来开放它自己的符号,必须定<BR>义这个宏。<BR> <BR> <BR> <BR>__GENKSYMS__<BR> <BR>当genksyms读入预处理文件来生成新的版本代码时,make定义了这个宏。当生成新的检<BR>验和时,该宏用于有条件的防止包含<linux/modversions.h>头文件。<BR> <BR> <BR> <BR>int get_persist(char *key, void *value, size_t length);<BR> <BR>int get_persist(char *key, void *value, size_t length);<BR> <BR> 对模块数据的永久性存储的支持依赖于这两个函数,它们在头文件<linux/kerne<BR>ld.h>中定义。<BR> <BR> <BR>-----------------------------------------------------------------------------<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P> 对模块数据的永久性存储的支持依赖于这两个函数,它们在头文件<linux/kerne<BR>ld.h>中定义。<BR> <BR> <BR>-----------------------------------------------------------------------------<BR> <BR>* 实际上,CRC算法检测不到SMP和非SMP模块间的不兼容性,因为许多接口函数都是内联<BR>(inline)的,它们在SMP和非SMP机器上时不同编译的,即使它们对应了同样的检验和。<BR>你必须非常小心地避免混淆SMP模块和常规的模块。<BR> <BR> <BR>--<BR><FONT
color=#00ff00>※ 来源:.华南网木棉站 bbs.gznet.edu.cn.[FROM: 202.38.196.234]</FONT><BR>--<BR><FONT
color=#00ffff>※ 转寄:.华南网木棉站 bbs.gznet.edu.cn.[FROM: 211.80.41.106]</FONT><BR>--<BR><FONT
color=#0000ff>※ 转寄:.华南网木棉站 bbs.gznet.edu.cn.[FROM: 211.80.41.106]</FONT><BR>--<BR><FONT
color=#ffff00>※ 转载:.南京大学小百合站 bbs.nju.edu.cn.[FROM: 211.80.41.106]</FONT><BR>--<BR><FONT
color=#ff0000>※ 转载:·饮水思源 bbs.sjtu.edu.cn·[FROM: 211.80.41.106]</FONT><BR></P></FONT>
<P align=center><A href="http://joyfire.net/lsdp/index.htm"><FONT
color=#ffffff size=2>目录页</FONT></A> | <A
href="http://joyfire.net/lsdp/13.htm"><FONT color=#ffffff
size=2>上一页</FONT></A> | <A href="http://joyfire.net/lsdp/15.htm"><FONT
color=#ffffff size=2>下一页</FONT></A></P></SPAN></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width="90%" align=center border=0>
<TBODY>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -