linux 禁用 内核 驱动程序,Linux设备驱动程序学习----5.模块的初始化和关闭

模块的初始化和关闭

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;

else

goto fail;

return 0; // 返回成功

fail:

my_cleanup();

return err; // 返回错误

}

如上代码所示,根据调用的注册/分配函数的语义,可以使用或不使用外部标记来标记每个初始化步骤的成功。这种方式的初始化能很好地扩展到对大量设施的支持。注意:因为清除函数被非退出代码调用,因此不能将清除函数标记为__exit;

4. 模块装载竞争

模块装载中也存在竞态。在模块注册完成之前,内核的某些部分可能会立即使用我们刚刚注册的任何设施,即在初始化函数还在运行的时候,内核就完全可能会调用我们的模块。因此,在首次注册完成之后,代码就应该准备好被内核的其他部分调用;在支持某个设施的所有内部初始化完成之前,不要注册任何设施。

当模块初始化失败而内核的某些部分已经使用了模块所注册的某个设施时,此时根本不应该出现模块初始化失败,因为模块已经成功导出了可用的功能及符号。如果初始化一定要失败,则应该仔细处理内核其他部分正在进行的操作,并且要等待这些操作的完成。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/487785.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python数据分析笔记中panda(2)

1 将手机号码分开为运营商,地区和号码段 1 from pandas import read_csv;2 3 df read_csv("H:\\pythonCode\\4.6\\data.csv");4 5 6 #转换成字符数据 方便用slice7 df[tel] df[tel].astype(str);8 9 #字符的抽取:根据已知列数…

刘忠范院士:新型研发机构建设成了口号

来源:科学网作者 | 郑金武编辑 | 宗华排版 | 华园● 刘忠范认为,如果只是单纯地再建一两所研究机构,在机制、理念上与现有的高校和研究院所没有差别,那就是在“1000”的基础上再加“1”,对现有的科研格局不会带来任何改…

launchMode

launchMode在多个Activity跳转的过程中扮演着重要的角色,它可以决定是否生成新的Activity实例,是否重用已存在的Activity实例,是否和其他Activity实例公用一个task里。这里简单介绍一下task的概念,task是一个具有栈结构的对象&…

linux属性表示的文件,Linux基础入门:文件和目录属性的含义

比如 :[rootistester isTester]# lltotal 12drwxr-xr-x 2 root root 4096 May 21 17:58 21Day-rw-r-xr-- 1 root root 6 May 14 16:04 idoxu.ini-rw-r--r-- 1 root root 0 May 21 17:12 istester.ini-rw-r--r-- 1 root root 10 May 14 16:02 README.md解释&…

一线工程师如何看待《没了美国的EDA软件,我们就不能做芯片了》

来源:真视界这些天看了不少讲国内EDA情况的帖子,有客观的也有极其离谱的,作为一名从业十余年的芯片设计工程师,我以一线从业者的角度来谈谈我们在实际工作中的EDA软件使用情况究竟是怎样的吧。先回答个很常见的问题:没…

2015Cocos游戏开发大赛作品——人鱼塞壬

***************************************转载请注明出处:http://blog.csdn.net/lttree******************************************** 消失了一个月,干啥去了捏? 就是做这个游戏了(事实上 考试课设北京。。。) 游戏背…

linux 文件 字符集设置,Linux字符集和系统语言设置-LANG,locale,LC_ALL,POSIX等命令及参数详解...

博文说明【前言】:本文将通过个人口吻介绍Linux字符集和系统语言设置,包括LANG,locale,LC_ALL,POSIX等命令及参数详解的相关知识,在目前时间点【2017年6月21号】下,所掌握的技术水平有限&#x…

柳叶刀发布陈薇团队新冠疫苗试验结果:安全,能诱导免疫反应

来源:腾讯新闻客户端自媒体论文称,研究显示,前述以腺病毒Ad5为载体的新冠疫苗,在给志愿者接种后28天时,显示出免疫原性和人体耐受性。在健康成年人中,对SARS-CoV-2的体液免疫反应,在接种疫苗后第…

UE4从4.15移植到4.16

如果是旧版本的工程需要移植到4.16,有几个地方需要修改: 假设RC是工程名,修改如下(三个CS文件) 类似的,插件也需要这样修改 转载于:https://www.cnblogs.com/AnKen/p/7365806.html

c语言Linux用线程创建文件,Linux环境下C语言线程创建---简单代码

在Linux环境下用C语言编写线程创建。//file name: pthreadtext.c#include #include //线程头文件//pthread不是linux下的默认的库,也就是在链接的时候,无法找到phread库中哥函数的入口地址,于是链接会失败//在gcc编译的时候,附加要…

自动驾驶发展到了哪个阶段?七大应用场景走进现实

来源: 智车科技2020年初,新冠疫情突发,百度、京东、美团等在各地提供无人配送、无人清扫服务。在抗击疫情的过程中,自动驾驶商业化得到了很好的实践验证。当自动驾驶技术渐渐走入现实场景,那么我们不禁要问这项技术究竟…

2016 博客导读总结 amp; 个人感悟

此文着笔之时。2017已经在眼前了。预计等我写完,2017已经到了。二次编辑于2017年1月1日早11点。 关于2016的感悟。十二月初就想写,当时认为是有点太早了,只是却思绪如泉涌。 且那时候才刚刚申请到博客专家(訪问量刚刚过5W&#x…

IBM 向云转型、大幅裁员、连 Watson 和 AI 团队也未能幸免

来源:云头条IBM提前30天通知成千上万名员工被裁,可领取90天的薪水,至少在美国是这样,而服务部门首当其冲。IBM正在大举裁员,数量众多的与云计算业务无关的员工被告知他们在蓝色巨人的时间到头了。这个IT巨头在回复IT外…

网络管理的任务包括linux,网络管理员的任务是阻止的10.152.8.0/21 一个基于Linux的防火墙的网络子网的默认端口上的所有出站SSH 连接。以下哪项规则集将完成这项任务?(单选题)...

_(12分)现用质量分数为98%、密度为1.84 g?cm-3的浓硫酸来配制500mL 0.2mol/L的稀H2SO4。可供选择的仪器有:①玻2016最新猴年5字春联清溪吟雅韵求下联把5.6g的Fe放入足量稀硫酸中,Fe完全反应.计算&#xff1…

linux网络包截获,用C实现截获网络数据包

现在进入第二步,捕获数据包。从第20行开始,我们进入了一个死循环,while(1),在第24行,recvfrom(sock, buffer, sizeof buffer, 0, (struct sockaddr *)&from, &fromlen),这个函数要做的就是接收数据…

vue框架的知识

基础:实例----组件----指令----选项-----计算属性----事件绑定----模板渲染-----内置动画 ---组件交互----路由。 vuejs干了什么事情:数据渲染/数据同步 组件化/模块化开发 其他:路由,ajax,数据流。 Vue实例对象&#…

自动驾驶的实现之路——几大关键传感器应用解析

来源:MEMS随着近两年来智慧汽车、车联网等等概念的兴起,汽车自动驾驶的各种科技进展不断占据媒体版面,引起了全球的关注和各国政府的支持。对于大部分人来说, “吃着火锅唱着歌”轻轻松松地直达目的地绝对是美好的愿望&#xff0c…

linux 退后根目录,linux下半部与退后执行的工作

表。当一个工作者线程被唤醒时,它会执行它的链表上的所有工作。当工作完毕时,他会将相应的work_struct对象从链表中移去。7.4.2 使用工作队列(1)创建推后的工作首先要做的是实际创建一些需要推后执行的工作。可以通过DECLARE_WORK在编译时静态的创建该结…

探索“植物人”语言能力和意识水平

来源:脑科学与智能技术卓越创新中心2020年5月25日,中国科学院脑科学与智能技术卓越创新中心(神经科学研究所)、中国科学院灵长类神经生物学重点实验室王立平研究组与复旦大学附属华山医院神经外科毛颖/吴雪海团队在《自然-神经科学…

linux多线程时序问题,Linux时序竞态问题(sleep函数的实现)

时序竞态是指同样的程序,多次调用运行的结果不同,这是由于争夺系统资源所造成的。比如说我们要使用alarm和pause函数来实现一个sleep的功能,那么由于alarm函数的实现过程并不是一个原子操作,那么随时可能被中断。比如说alarm了1秒…