二 Linux设备驱动程序——建立和运行模块( 二 )


模块初始化函数注册模块提供的任何功能,实际的初始化函数定义常常如:
static int __init initialization_function(void){/* Initialization code here */}module_init(initialization_function);
1、清理函数
每个非试验性的模块也要求有一个清理函数,它注销接口,在模块被去除之前返回所有资源给系统 。这个函数定义为:
static void __exit cleanup_function(void){/* Cleanup code here */}module_exit(cleanup_function);
清理函数没有返回值, 因此它被声明为 void,修饰符标识这个代码是只用于模块卸载(通过使编译器把它放在特殊的 ELF 段),如果你的模块直接建立在内核里,或者如果你的内核配置成不允许模块卸载,标识为的函数被简单地丢弃 。因为这个原因,一个标识的函数只在模块卸载或者系统停止时调用;任何别的使用是错的 。再一次,声明对于使得内核能够找到你的清理函数是必要的 。
2、初始化中的错误处理
你必须记住一件事,在注册内核设施时,注册可能失败 。即便最简单的动作常常需要内存分配,分配的内存可能不可用 。因此模块代码必须一直检查返回值,并且确认要求的操作实际上已经成功 。
int __init my_init_function(void){int err;err = register_this(ptr1, "skull"); /* registration takes a pointer and a name */if (err)goto fail_this;err = register_that(ptr2, "skull");if (err)goto fail_that;err = register_those(ptr3, "skull");if (err)goto fail_those;return 0; /* success */fail_those:unregister_that(ptr2, "skull");fail_that:unregister_this(ptr1, "skull");fail_this:return err; /* propagate the error */}
模块清理函数必须撤销任何由初始化函数进行的注册,并且惯例(但常常不是要求的)是按照注册时相反的顺序注销设施 。
void __exit my_cleanup_function(void){unregister_those(ptr3, "skull");unregister_that(ptr2, "skull");unregister_this(ptr1, "skull");return;}
如果你的初始化和清理比处理几项复杂,goto 方法可能变得难于管理,因为所有的清理代码必须在初始化函数里重复,有时包括几个混合的标号,因此,一种不同的代码排布证明更成功 。
使代码重复最小和所有东西流线化,你应当做的是无论何时发生错误都从初始化里调用清理函数 。清理函数接着必须在撤销它的注册前检查每一项的状态,以最简单的形式,代码看起来象这样:
struct something *item1;struct somethingelse *item2;int stuff_ok;void my_cleanup(void){if (item1)release_thing(item1);if (item2)release_thing2(item2);if (stuff_ok)unregister_stuff();return;}int __init my_init(void){int err = -ENOMEM;item1 = allocate_thing(arguments);item2 = allocate_thing2(arguments2);if (!item2 || !item2)goto fail;err = register_stuff(item1, item2);if (!err)stuff_ok = 1;elsegoto fail;return 0; /* success */fail:my_cleanup();return err;}
清理函数当由非退出代码调用时不能标志为。
3、模块加载竞争
内核的某些别的部分会在注册完成之后马上使用任何你注册的设施,这是完全可能的,换句话说,内核将调用进你的模块,在你的初始化函数仍然在运行时,所以你的代码必须准备好被调用,一旦它完成了它的第一个注册 。不要注册任何设施,直到所有的需要支持那个设施的你的内部初始化已经完成 。
八、模块参数
模块参数可以在运行或命令装载模块时赋值,可以从配置文件(/etc/.conf)中读取参数值 。
在改变模块参数之前,模块必须让参数对命令可见 。参数使用 (变量名,类型,访问许可值)宏来声明,它定义在 .h 。