2.6驱动移植系列之Getting started(3)-- more ..
时间:2007-03-17 来源:lanttor.guo
翻译:山涛
联系邮件:[email protected]
Driver porting: more module changes
[Posted February 11, 2003 by corbet]
The first article in this series noted a couple of changes that result from the new, kernel-based module loader. In particular, explicit module_init() and module_exit() declarations are now necessary. Quite a few other things have changed as well, however; this article will summarize the most important of those changes.
这个文章系列的第一篇描述了新内核模块加载器的一些变化。尤其,显示的使用module_init()和module_exit()在编写驱动模块是必须的。然而,还有很多其他的关于编写驱动模块的变化,本篇将详细描述它们。
Module parameters
The old MODULE_PARM macro, which used to specify parameters which can be passed to the module at load time, is no more. The new parameter declaration scheme add type safety and new functionality, but at the cost of breaking compatibility with older modules.
Modules with parameters should now include <linux/moduleparam.h> explicitly. Parameters are then declared with module_param:
module_param(name, type, perm);
Where name is the name of the parameter (and of the variable holding its value), type is its type, and perm is the permissions to be applied to that parameter's sysfs entry. The type parameter can be one of byte, short, ushort, int, uint, long, ulong, charp, bool or invbool. That type will be verified during compilation, so it is no longer possible to create confusion by declaring module parameters with mismatched types. The plan is for module parameters to appear automatically in sysfs, but that feature had not been implemented as of 2.6.0-test9; for now, the safest alternative is to set perm to zero, which means "no sysfs entry."
模块参数
原来的宏 MODULE_PARM,在加载模块时用于传递参数,现在不再使用。新的参数声明方案增加了类型安全和新的功能,但是代价是破坏了旧模块的兼容性。
使用模块参数,需要显示地包含<linux/moduleparam.h>文件,参数通过 module_param 进行声明:
module_param(name, type, perm);
name是参数名(也是用于赋值的变量名),type是参数的类型,perm是参数的sysfs入口项的许可掩码。type参数可以选择以下的数据类型:byte、 short、ushort、 int、 uint、 long、 ulong、 charp、 bool 或者 invbool。type将在模块编译的时候被验证,因此当传递的参数类型不匹配时不会产生混淆。计划模块参数将自动出现在sysfs里,但是这个特性在 2.6.0-test9 版本里还没有实现。现在,我们只需要将perm安全的设置为0即可,它表示“没有sysfs入口项”。
If the name of the parameter as seen outside the module differs from the name of the variable used to hold the parameter's value, a variant on module param may be used:
module_param_named(name, value, type, perm);
Where name is the externally-visible name and value is the internal variable.
如果在模块外部看到的参数名与模块内部使用的参数的变量名不同的话,我们可以使用下面的参数声明函数:
module_param_named(name, value, type, perm); (译者:这表示,我们可以在设置参数的时候,使参数名和变量名不一样)
String parameters will normally be declared with the charp type; the associated variable is a char pointer which will be set to the parameter's value. If you need to have a string value copied directly into a char array, declare it as:
module_param_string(name, string, len, perm);
Usually, len is best specified as sizeof(string).
字符串参数将使用charp类型声明;与之相关的变量是一个char指针,用于设置参数的值。如果你需要将一个字符串值直接拷贝到一个char数组里,可以这样定义:
module_param_string(name, string, len, perm); 通常,len最好这样设置成 sizeof (string)。
Finally, array parameters (supplied at module load time as a comma-separated list) may be declared with:
module_param_array(name, type, num, perm);
The one parameter not found in module_param() (num) is an output parameter; if a value for name is supplied when the module is loaded, num will be set to the number of values given. This macro uses the declared length of the array to ensure that it is not overrun if too many values are provided.
最后,数组参数(在加载模块的时候,作为逗号隔开的列表提供)可以这样定义: module_param_array(name, type, num, perm); num参数是一个输出参数,它表示数组参数的长度。它保证了加载模块时,加载器不会接受超过num所规定的数值。
As an example of how the new module parameter code works, here is a paramaterized version of the "hello world" module shown previously:
下面是一个新的模块参数工作的例子,是基于之前提到的”hello world”版本的修改版:
#include <linux/init.h> #include <linux/module.h> #include <linux/moduleparam.h> MODULE_LICENSE("Dual BSD/GPL"); /* * A couple of parameters that can be passed in: how many times we say * hello, and to whom. */ static char *whom = "world"; module_param(whom, charp, 0); static int howmany = 1; module_param(howmany, int, 0); static int hello_init(void) { int i; for (i = 0; i < howmany; i++) printk(KERN_ALERT "(%d) Hello, %s\n", i, whom); return 0; } static void hello_exit(void) { printk(KERN_ALERT "Goodbye, cruel %s\n", whom); } module_init(hello_init); module_exit(hello_exit);
Inserting this module with a command like:
象这样加载模块:
insmod ./hellop.ko howmany=2 whom=universe
causes the message "hello, universe" to show up twice in the system logfile.
在系统日志上,会输出“hello, universe”两次。
Module aliases
A module alias is an alternative name by which a loadable module can be known. These aliases are typically defined in /etc/modules.conf, but many of them are really a feature of the module itself. In 2.6, module aliases can be embedded with a module's source. Simply add a line like:
MODULE_ALIAS("alias-name"); 模块别名 一个模块的别名是一个加载模块的另外一个名字。这些别名一般典型的在/etc/modules.conf中定义,但是许多模块将这个定义作为本身的一个特征在模块里定义。在kernel2.6里,模块别名可以嵌入在模块的代码里。简单的如下使用: MODULE_ALIAS("alias-name");
The module use count
In 2.4 and prior kernels, modules maintained their "use count" with macros like MOD_INC_USE_COUNT. The use count, of course, is intended to prevent modules from being unloaded while they are being used. This method was always somewhat error prone, especially when the use count was manipulated inside the module itself. In the 2.6 kernel, reference counting is handled differently. 在2.4及其之前的内核里,模块(们)使用宏 MOD_INC_USE_COUNT 维护它们的“用户计数”。用户计数目的在于防止当模块在使用时被卸载掉。这种方法常常易于出错的,特别是当用户计数被模块自己本身所维护时。在kernle2.6里,对模块的引用计数(译者:引用计数和模块计数在这里是相同的意思)的处理方法和之前完全不同了。 The only safe way to manipulate the count of references to a module is outside of the module's code. Otherwise, there will always be times when the kernel is executing within the module, but the reference count is zero. So this work has been moved outside of the modules, and life is generally easier for module authors. 对模块的使用唯一的安全方法是在模块之外维护其使用计数,否则,会经常出现这样的情况:当内核正在执行模块时,而模块的使用计数却是0。因此,这项工作被移植到模块之外,对于模块的编写者来说他们的生活将轻松一些了:)。
Any code which wishes to call into a module (or use some other module resource) must first attempt to increment that module's reference count:
int try_module_get(&module);
It is also necessary to look at the return value from try_module_get(); a zero return means that the try failed, and the module should not be used. Failure can happen, for example, when the module is in the process of being unloaded.
任何代码想使用一个模块(或者使用其他模块资源)必须首先尝试利用下面的函数来增加模块的引用计数: int try_module_get(&module); 它当然必须检查try_module_get()的返回值;返回值为0表示引用该模块的尝试失败,模块不会被使用;比如,当需要引用的模块没有被加载时,这种错误就会发生。
A reference to a module can be released with module_put().
Again, modules will not normally have to manage their own reference counts. The only exception may be if a module provides a reference to an internal data structure or function that is not accounted for otherwise. In that (rare) case, a module could conceivably call try_module_get() on itself.
对模块的引用的释放通过函数module_put()来实现。 重述一遍,模块将不必要自己管理自己被引用的计数。只有一个例外,如果这个模块的使用是提供给模块自己内部的数据结构的话,这种情况下(这种情况的几率非常少),模块可以自己本身使用try_module_get()函数。 As of this writing, modules are considered "live" during initialization, meaning that a try_module_get() will succeed at that time. There is still talk of changing things, however, so that modules are not accessible until they have completed their initialization process. That change will help prevent a whole set of race conditions that come about when a module fails initialization, but it also creates difficulties for modules which have to be available early on. For example, block drivers should be available to read partition tables off of disks when those disks are registered, which usually happens when the module is initializing itself. If the policy changes and modules go back off-limits during initialization, a call to a function like make_module_live() may be required for those modules which must be available sooner. (Update 2.6.0-test9: this change has not happened and seems highly unlikely at this point). 在写这篇文章的时候,模块在初始化期间被认为是“激活的”,这意味着在这个时候调用try_module_get()将会成功。然而,模块只有完全完成它们的初始化之后才能被访问,这样可以避免当模块初始化失败后一系列竞态条件的发生,但同时这也给最开始必须加载成功的模块带来了困难。例如,当磁盘驱动注册成功后,块设备应当可以有效的读取磁盘的分区表,但这通常需要磁盘驱动初始化完全成功后。(译者:这个问题说明,当我们在注册一个设备之前,必须完成它的初始化,而起初始化必须成功)。如果将来驱动开发的策略需要更改以及模块退回到在初始化时需要限制的情况下,那么象make_module_live()这样的函数需要被实现,用于那些必须尽早使能有效的模块。(2.6.0-test9: 在这个版本上并没有实现这个提议)
Exporting symbols
For the most part, the exporting of symbols to the rest of the kernel has not changed in 2.6 - except, of course, for the fact that any user of those symbols should be using try_module_get() first. In older kernels, however, a module which did not arrange things otherwise would implicitly export all of its symbols. In 2.6, things no longer work that way; only symbols which have explicitly been exported are visible to the rest of the kernel.
导出符号
大体而言,在2.6内核版本,符号导出对于内核的其他代码并没有带来什么变化--当然,想使用符号的用户应当首先调用try_module_get()函数。较老的内核,模块默认情况下(隐式的)所有符号都是导出的。在2.6,这种方式不再工作,只有显示的被声明为导出的符号才能被内核的其他代码可见。
Chances are that change will cause few problems. When you get a chance, however, you can remove EXPORT_NO_SYMBOLS lines from your module source. Exporting no symbols is now the default, so EXPORT_NO_SYMBOLS is a no-op.
理由是这样的变化将不会引起问题。现在,你有机会从你的模块代码中移除掉 EXPORT_NO_SYMBOLS 这一句。现在模块默认情况是不导出任何符号,所以 EXPORT_NO_SYMBOLS 是一个无用的操作。
The 2.4 inter_module_ functions have been deprecated as unsafe. The symbol_get() function exists for the cases when normal symbol linking does not work well enough. Its use requires setting up weak references at compile time, and is beyond the scope of this document; there are no users of symbol_get() in the 2.6.0-test9 kernel source.
2.4内核这个的内部函数inter_module_因为其不安全性而遭到反对。当符号链接不能很好的工作时,symbol_get()这个函数用于处理这种情况。这个函数的使用需要在编译的时候设置“弱引用”,这个问题的讨论超出了本篇的范围。在 2.6.0-test9 kernel source 里没有使用symbol_get()。
Kernel version checking
2.4 and prior kernels would include, in each module, a string containing the version of the kernel that the module was compiled against. Normally, modules would not be loaded if the compile version failed to match the running kernel.
2.4和之前的内核在每个模块里包含一个字符串,这个字符串描述了模块编译时内核的版本。正常情况下,如果编译的版本和正在运行的内核版本不匹配时,模块将不能被加载。
In 2.5, things still work mostly that way. The kernel version is loaded into a separate, "link-once" ELF section, however, rather than being a visible variable within the module itself. As a result, multi-file modules no longer need to define __NO_VERSION__ before including <linux/module.h>.
在2.5中,仍然以这样的方式工作。然而,内核版本信息被加载到一个独立的“一次链接”的ELF 段,而不是象以前一样在模块里定义这样的一个变量来描述内核版本信息(译者:参阅vermagic.o的内容)。结果,多文件的模块就不再需要在包含<linux/module.h>之前定义__NO_VERSION__ 宏了。
The new "version magic" scheme also records other information, including the compiler version, SMP status, and preempt status; it is thus able to catch more incompatible situations than the old scheme did.
新的“version magic”方法也记录了其他的信息,包含了编译器版本、SMP状态、抢占状态。比起旧的策略,它能捕捉取更多的不兼容条件。
Module symbol versioning ("modversions") has been completely reworked for the 2.6 kernel. Module authors who use the makefiles shipped with the kernel (and that is about the only way to work now) will find that dealing with modversions has gotten easier than before. The #define hack which tacked checksums onto kernel symbols has gone away in favor of a scheme which stores checksum information in a separate ELF section.
模块符号版本倚赖(”modversions”)已经可以完全在2.6内核工作了。使用跳转到内核进行模块编译的makefile的模块作者会发现现在比以前更容易处理modversion的问题了(事实上,模块作者只能使用这样的方式了)。使用#define定义内核验证信息的方法不再使用了,因为现在新的方法将验证信息都放在了一个独立的ELF段了。