上海房产网站建设,phpcms怎么做网站,交友平台网站建设,重庆市建设公共资源交易中心网站首页模块的初始化和关闭1. 初始化函数模块的初始化函数负责注册模块所提供的任何设施#xff0c;即可以被应用程序访问的新功能#xff0c;可能是一个完整的驱动程序或者仅仅是一个新的软件抽象。初始化函数的定义通常如下所示#xff1a;static int __init initialization_func…模块的初始化和关闭1. 初始化函数模块的初始化函数负责注册模块所提供的任何设施即可以被应用程序访问的新功能可能是一个完整的驱动程序或者仅仅是一个新的软件抽象。初始化函数的定义通常如下所示static int __init initialization_function(void){// 初始化代码return 0;}module_init(initialization_function);初始化函数被声明为static因为初始化函数在特定文件之外没有其他意义。__init标记表明该函数仅在初始化期间使用。在模块被装载之后模块装载器就会将初始化函数扔掉可将该函数占用的内存释放出来。注意不要在结束初始化之后仍要使用的函数或数据结构上使用__init和__initdata标记。对于__devinit和__devinitdata只有在内核未被配置为支持热插拔设备的情况下才会被翻译为__init和__initdata。module_init()宏的使用是强制性的会在模块的目标代码中增加一个特殊的段用于说明内核初始化函数所在的位置。如果没有这个定义初始化函数永远不会被调用。2. 清除函数每个模块都需要一个清除函数在模块被移除前注销接口并向系统中返回所有资源。该函数定义如下static void __exit cleanup_function(void){// 清除代码}module_exit(cleanup_function);清除函数没有返回值__exit修饰词标记该代码仅用于模块卸载编译器会把该函数放在特殊的ELF段中。如果模块被直接编译到内核中或者内核配置不允许卸载模块则被标记为__exit的函数将被直接丢弃。所以被标记为__exit的函数只能在模块被卸载或者系统关闭时被调用其他任何用法都是错的。module_exit()声明对于内核找到模块的清除函数是必需的。如果一个模块未定义清除函数则内核不允许卸载该模块。3. 初始化过程中的错误处理在内核中注册设施时注册可能会失败。即使最简单的动作都需要内存分配而所需的内存可能无法获得。因此模块代码必须始终检查返回值并确保所请求的操作已真正执行成功。如果在注册设施时遇到错误首先要判断模块是否可以继续初始化只要可能模块应该继续向前并尽可能提供其功能。如果在发生了某个特定类型的错误之后无法继续装载模块则要将出错之前的所有注册工作都撤销掉。即当模块的初始化出现错误之后模块必须自行撤销已注册的设施。如果未能撤销已注册的设施则内核会处于一种不稳定状态这时唯一有效的解决办法就是重新引导系统。所以必须在初始化过程出现错误时认真完成正确的工作。错误恢复的处理有时使用goto语句非常有效。正常情况下很少使用goto但是唯一在错误处理时却非常有效。内核经常使用goto来处理错误。如下例子所示int __init my_init_function(void){int err;// 使用指针和名称注册err register_this(ptr1, skull);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; // 成功fail_those:unregister_that(ptr2, skull);fail_that:unregister_that(ptr1, skull);fail_this:return err; // 返回错误}在出错的时候使用goto语句将只撤销出错时刻以前所成功注册的那些设施。另一种方法是记录任何成功注册的设施在出错的时候调用模块的清除函数。清除函数将仅仅回滚已成功完成的步骤。这种方法需要更多的代码和CPU时间因此在追求效率的代码中使用goto语句是最好的错误恢复机制。在Linux内核中错误编码是定义在头文件中的负整数如果不想使用其他函数返回的错误码应该包含头文件以使用如-ENODEV、-ENOMEM之类的符号值。每次返回核时的错误编码是个好习惯因为用户程序可以通过perror()函数或类似途径将错误符号转换为有意义的字符串。模块的清除函数需要撤销初始化函数所注册的所有设施并且习惯上以相反于注册的顺序撤销设施如下所示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;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 (!item1 || !item2)goto fail;err register_stuff(item1, item2);if (!err)stuff_ok 1;elsegoto fail;return 0; // 返回成功fail:my_cleanup();return err; // 返回错误}如上代码所示根据调用的注册/分配函数的语义可以使用或不使用外部标记来标记每个初始化步骤的成功。这种方式的初始化能很好地扩展到对大量设施的支持。注意因为清除函数被非退出代码调用因此不能将清除函数标记为__exit4. 模块装载竞争模块装载中也存在竞态。在模块注册完成之前内核的某些部分可能会立即使用我们刚刚注册的任何设施即在初始化函数还在运行的时候内核就完全可能会调用我们的模块。因此在首次注册完成之后代码就应该准备好被内核的其他部分调用在支持某个设施的所有内部初始化完成之前不要注册任何设施。当模块初始化失败而内核的某些部分已经使用了模块所注册的某个设施时此时根本不应该出现模块初始化失败因为模块已经成功导出了可用的功能及符号。如果初始化一定要失败则应该仔细处理内核其他部分正在进行的操作并且要等待这些操作的完成。