📄 (ldd) ch11-kerneld和高级模块化(转载).txt
字号:
* 生成校验和(定义了*__GENKSYMS__)
*/
#if defined(MODVERSIONS) && !defined(__GENKSYMS__)
# include <linux/modversions.h>
# include "export.ver" /* 为了包含CRC,重定义了
"export_function" */
"export_function" */
#endif
这些代码,虽然令人讨厌,但好处是:可以让Makefile处于一个干净的状态。另一方面
,由make来传递正确的标志,涉及到为各种情况编写冗长的命令行,因此我就不在这里
做了。
简单的import模块通过传递数字2和2作为参数,来调用export_function;期望的结果是
4。下面的例子显示import确实链接到了export的版本化符号,并且调用了函数。版本化
符号出现在/proc/ksyms文件中。
morgana.root# insmod export
morgana.root# grep export /proc/ksyms
0202d024 export_function_R2eb14c1e (export)
0202d024 export_function_R2eb14c1e (export)
morgana.root# insmod import
import my mate tells that 2+2 = 4
morgana.root# cat /proc/modules
import 1 0
export 3 [import] 0
跨过卸载/装载的持久存储
一旦我们装备上了kerneld和版本支持,就会发现使用模块比使用链进内核的驱动程序更
方便。模块化只有一个问题:如果一个驱动程序由kerneld载入,然后被配置(通过ioct
l或者其它方法),那么下次将该驱动程序载入内核时又必须重新配置它。而启动时的配
置信息则可以在/etc/modules.conf文件中一劳永逸的指定,因此当要多次使用按需装载
时,运行时的配置变得容易丧失。用户会可能会失望地发现刚离开休息一会设备的配置
信息就已经丢失了。我们需要的是一种可以在模块卸载后持久地保存相关信息的技术。
实际上,modules包从2.0.0版开始(modules-2.2.0)提供这种能力。
实际上,modules包从2.0.0版开始(modules-2.2.0)提供这种能力。
真正的代码还没有集成进官方的内核,但很可能会被Linus的源码所接受。目前
,为了启动对持久存储的支持,你需要使用modules包中发布的一个补丁;这个补丁在<l
inux/kerneld.h>中添加了几行代码。
实际上,隐藏在模块信息的持久存储之后的想法很直接:与用户空间相互传输信息,内
核代码可以与转载 托对啬 块使用同一个kerneld引擎。然后,守护进程使用一个通用数
据库来管理信息存储。
在用户空间而不是在内核空间实现持久存储的原因是为了简化代码。尽管可以设计出仅
与内核空间有关的实现,从内核空间访问一个数据库文件需要将库代码在不可交换的内
核空间中复制,而在用户空间,库代码的使用则没有任何开销。
Kerneld中提议的实现使用了gdbm库来实现数据库。也可以选择使用盘上数据库。如果使
Kerneld中提议的实现使用了gdbm库来实现数据库。也可以选择使用盘上数据库。如果使
用了该数据库,就可以获得跨过系统启动的持久性存储;如果没有使用该数据库,你只
能在kerneld进程的生存期内获得持久性。
下面这些函数是在头文件<linux/kerneld.h>中定义的,用于获得持久存储特性:
int set_persist(char *key, void *value, size_t length);
int get_persist(char *key, void *value, size_t length);
这些函数的参数是一个文本关键字(key)和数据项本身-该关键字唯一地标记数据库中
的一个数据项,而数据项则呈现为一个指针和长度的熟悉形式。参数key在整个系统内都
必须唯一。这样就允许每个模块通过在关键字前加上模块名字将自己的关键字分离出来
,但是它也允许不同的模块共享配置变量,如果因为什么原因需要这么做的话。
可能的返回值和其它调用kerneld-_send的函数是一样的:0表示成功,负数通知一个内
核空间的错误,而正数用于通知一个用户空间的错误。通常可以忽略返回值,因为如果
有错的话,get_persist不会修改value的值,而如果set_persist不能保存这个值的话,
也不会做任何事情。
新近的kerneld守护进程开始支持这种新特性,所以模块也可以选择在内核中不对kernel
d.h做修补而将set_persist和get_persist的定义包含进来。但要注意向前兼容。建议使
用在modules中发布的补丁;在被官方的内核源代码中包含前持久存储的内部实现可能会
有变化。
我们已经看到,使用持久存储的主要原因是避免每次把模块载入一个运行内核时又要重
新配置它。这对按需装载的模块的运行时配置尤其重要;这对装载时配置也是一个有意
义的选项,因为更新/etc/modules.conf对普通用户来说有些复杂。
持久存储另一个可能的用处是跟踪系统的硬件配置以避免不必要的探测。探测硬件是一
种冒险的操作。它可能会错误地配置了其它的硬件,特别是对ISA设备,因为ISA不象PCI
种冒险的操作。它可能会错误地配置了其它的硬件,特别是对ISA设备,因为ISA不象PCI
那样提供了一种通用的方法来扫描系统总线。(第15章“外设总线概貌”详细讨论了该
问题。)
下面的例子代码显示了一个名为psm(Persistent Storage Module)的假想模块是如何
避免不必要的探测的。为简化讨论,这个例子程序最多支持一个设备。
int psm_base = 0; /* 基本的I/O端口,在装载时可以设定 */
int init_module(void)
{
if (psm_base==0){ /* 在装载时没有设定 */
get_persist("psm_base", &psm_base, sizeof(int));
if (psm_check_hw(psm_base)!=0)
psm_base=0; /* 旧的数值不再有效:探测 */
}
else
if (psm_check_hw(psm_base)!=0)
return -ENODEV; /* 没有任何地方指明基地址 */
if (psm_base==0)
psm_base=psm_probe(); /* 返回基本端口,或者,如果没能找
到就返回0 */
if (psm_base==0)
return -ENODEV;/* 没有找到任何设备 */
return -ENODEV;/* 没有找到任何设备 */
set_persist("psm_base", &psm_base, sizeof(int));/* 找到:保存它 */
}
只有在装载时没有指定基本端口,并且以前的端口不再有效的时候,这些代码才探测硬
件。如果找到一个设备,基本端口被保存起来,留作后用。
当驱动程序要支持多个设备时,检测新增加的硬件这个问题的一个可能的办法就是定义
一个psm_newhw变量,如果添加了新设备到系统,那么用户可以在装载时对该变量进行设
置。如果这样实现的话,那么当存在新设备时,用户必须使用insmod psm_newhw=1命令
。如果psm_newhw不为0,init_module试着探测新设备,而在通常情况下它使用的是保存
的信息。一个设备的基地址中的改变在上面给出的代码中已经处理过了,而不需要用户
在装载时进行干预。
快速参考
快速参考
本章引入下面一些内核符号:
/etc/modules.conf
modprobe和depmod程序的配置文件。它用于配置按需加载,在这两个程序的man页中有描
述。
#include <linux/kerneld.h>
int request_module(const char *name);
int release_module(const char *name, int waitflag);
int delayed_release_module(const char *name);
int cancel_release_module(const char *name);
这些函数通过kerneld守护进程进行模块的按需加载。
这些函数通过kerneld守护进程进行模块的按需加载。
#include <linux/autoconf.h>
CONFIG_MODVERSIONS
只有当前内核被编译成支持版本化符号时这个宏才会被定义。
#ifdef MODVERSIONS
#include <linux/modversions.h>
这个头文件只有在CONFIG_MODVERSIONS有效时才存在,它包含了内核开放的所有符号的
版本化名字。
EXPORT_SYMTAB
如果使用了版本支持并且你的模块使用了register_symtab来开放它自己的符号,必须定
义这个宏。
__GENKSYMS__
当genksyms读入预处理文件来生成新的版本代码时,make定义了这个宏。当生成新的检
验和时,该宏用于有条件的防止包含<linux/modversions.h>头文件。
int get_persist(char *key, void *value, size_t length);
int get_persist(char *key, void *value, size_t length);
对模块数据的永久性存储的支持依赖于这两个函数,它们在头文件<linux/kerne
ld.h>中定义。
-----------------------------------------------------------------------------
对模块数据的永久性存储的支持依赖于这两个函数,它们在头文件<linux/kerne
ld.h>中定义。
-----------------------------------------------------------------------------
* 实际上,CRC算法检测不到SMP和非SMP模块间的不兼容性,因为许多接口函数都是内联
(inline)的,它们在SMP和非SMP机器上时不同编译的,即使它们对应了同样的检验和。
你必须非常小心地避免混淆SMP模块和常规的模块。
--
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -