linux初始化中的错误处理
你必须记住一件事, 在注册内核设施时, 注册可能失败. 即便最简单的动作常常需要内存 分配, 分配的内存可能不可用. 因此模块代码必须一直检查返回值, 并且确认要求的操作 实际上已经成功.
如果在你注册工具时发生任何错误, 首先第一的事情是决定模块是否能够无论如何继续初 始化它自己. 常常, 在一个注册失败后模块可以继续操作, 如果需要可以功能降级. 在任 何可能的时候, 你的模块应当尽力向前, 并提供事情失败后具备的能力.
如果证实你的模块在一个特别类型的失败后完全不能加载, 你必须取消任何在失败前注册 的动作. 内核不保留已经注册的设施的每模块注册, 因此如果初始化在某个点失败, 模块 必须能自己退回所有东西. 如果你无法注销你获取的东西, 内核就被置于一个不稳定状态; 它包含了不存在的代码的内部指针. 这种情况下, 经常地, 唯一的方法就是重启系统. 在 初始化错误发生时, 你确实要小心地将事情做正确.
错误恢复有时用 goto 语句处理是最好的. 我们通常不愿使用 goto, 但是在我们的观念里, 这是一个它有用的地方. 在错误情形下小心使用 goto 可以去掉大量的复杂, 过度对齐的, "结构形" 的逻辑. 因此, 在内核里, goto 是处理错误经常用到, 如这里显示的.
下面例子代码( 使用设施注册和注销函数)在初始化在任何点失败时做得正确:
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 */
}
这段代码试图注册 3 个(虚构的)设施. goto 语句在失败情况下使用, 在事情变坏之前只 对之前已经成功注册的设施进行注销.
另一个选项, 不需要繁多的 goto 语句, 是跟踪已经成功注册的, 并且在任何出错情况下 调用你的模块的清理函数. 清理函数只回卷那些已经成功完成的步骤. 然而这种选择, 需 要更多代码和更多 CPU 时间, 因此在快速途径下, 你仍然依赖于 goto 作为最好的错误恢 复工具.
my_init_function 的返回值, err, 是一个错误码. 在 Linux 内核里, 错误码是负数, 属 于定义于 <linux/errno.h> 的集合. 如果你需要产生你自己的错误码代替你从其他函数得 到的返回值, 你应当包含 <linux/errno.h> 以便使用符号式的返回值, 例如 -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; 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;
else
goto
fail;
return
0; /* success */
fail:
}
my_cleanup();
return err;
如这段代码所示, 你也许需要, 也许不要外部的标志来标识初始化步骤的成功, 要依赖你 调用的注册/分配函数的语义. 不管要不要标志, 这种初始化会变得包含大量的项, 常常比 之前展示的技术要好. 注意,
但是, 清理函数当由非退出代码调用时不能标志为 exit, 如同前面的例子.
linux初始化中的错误处理的更多相关文章
- Linux C中结构体初始化
在阅读GNU/Linux内核代码时,我们会遇到一种特殊的结构初始化方式.该方式是某些C教材(如谭二版.K&R二版)中没有介绍过的.这种方式称为指定初始化(designated in ...
- 【转】【整理】将Linux脚本中的正常输出,警告,错误等信息输出到文件中
本文来自:http://blog.csdn.net/woshinia/article/details/18040063 很早以前 编译的时候 就在用 2>&1,但是一直没有生成一 ...
- 【Azure 应用服务】PHP应用部署在App Service for Linux环境中,上传文件大于1MB时,遇见了413 Request Entity Too Large 错误的解决方法
问题描述 在PHP项目部署在App Service后,上传文件如果大于1MB就会遇见 413 Request Entity Too Large 的问题. 问题解决 目前这个问题,首先需要分析应用所在的 ...
- Linux下的段错误(Segmentation fault)
Linux开发中常见段错误问题原因分析 1 使用非法的内存地址(指针),包括使用未经初始化及已经释放的指针.不存在的地址.受系统保护的地址,只读的地址等,这一类也是最常见和最好解决的段错误问题,使用G ...
- 浅析 Linux 初始化 init 系统,第 1 部分: sysvinit 第 2 部分: UpStart 第 3 部分: Systemd
浅析 Linux 初始化 init 系统,第 1 部分: sysvinit 第 2 部分: UpStart 第 3 部分: Systemd http://www.ibm.com/developerw ...
- Linux 编程中的API函数和系统调用的关系【转】
转自:http://blog.chinaunix.net/uid-25968088-id-3426027.html 原文地址:Linux 编程中的API函数和系统调用的关系 作者:up哥小号 API: ...
- 【转】linux驱动程序中的并发控制
原文网址:http://www.cnblogs.com/geneil/archive/2011/12/03/2274684.html 现代操作系统有三大特性:中断处理.多任务处理和多处理器.这些特性导 ...
- Linux内核中流量控制
linux内核中提供了流量控制的相关处理功能,相关代码在net/sched目录下:而应用层上的控制是通过iproute2软件包中的tc来实现, tc和sched的关系就好象iptables和netfi ...
- 【ASP.NET Core】在Win 10 的 Linux 子系统中安装 .NET Core
在上一篇文章中,老周扯了一下在 Windows 10 中开启 Linux 子系统,并且进行了一些简单的设置.本篇咱们就往上面安装 .net core . 老周假设你从来没有用过 Linux,所以,接着 ...
随机推荐
- php_imagick是怎么实现复古效果的呢?
php_imagick程序示例 1.创建一个缩略图并显示出来 <?phpheader('Content-type: image/jpeg');$image = new Imagick('imag ...
- text-align:justify在项目中碰到的问题
最近在项目中,使用了一个新的样式属性:text-align:justigy,这个属性在使用过程中遇到了一些小异常,现在总结下. text-align有一个属性值为justify,为对齐之意.其实现的 ...
- Rol租车网项目总结
现在自行车的租聘如此火爆,我们是否需要加入这个浩浩荡荡的行列? 相比起现在ofo共享单车,摩拜单车.而我们的竞争力在何处? 如何能让我们的项目脱颖而出? 而我们的Rol租车网为什么要叫Rol呢? Ri ...
- 【JZOJ4791】【NOIP2016提高A组模拟9.21】矩阵
题目描述 在麦克雷的面前出现了一个有n*m个格子的矩阵,每个格子用"."或"#"表示,"."表示这个格子可以放东西,"#" ...
- python 数据分析领域常用的包
- 程序中提醒用户进去App Store 评分 跳转 代码
大家都知道,评论和评分是决定app在appstore中排名的重要因素,但是大部分用户下载安装APP后却不会去点评,所以添加提示用户去点评的功能是很必要的,如下是代码: 很多用户用了好软件 ...
- PHP Laravel系列之环境搭建( VirtualBox+Vagrant+Homestead+系列网址)
搭建环境从来都是阻挡一门新技能的最致命的硬伤,为了这个环境,我又是花费了半天的时间,各种问题层出不穷,下面基于网上的一些教程(我看到的都多少有些问题) 开始的时候是在实验楼这个平台上开始学习的,不过 ...
- 使用R拟合分布
使用R拟合分布 几个常用的概率函数介绍 这里,参考R语言实战,以及[Fitting Distribution with R]的附录. 一.认识各种分布的形态 1.1 连续型随机变量的分布 首先,我们来 ...
- laravle 事务
DB::beginTransaction(); try{ $name = 'abc'; $result1 = Test::create(['name'=>$name]); ...
- F4NNIU 整理的 docker-compose 几个常用命令
F4NNIU 整理的 docker-compose 几个常用命令 up 创建并启动一个容器. 记录一下,如果已经有容器,up 和 start 都可以启动容器,up 可以看到调试窗口,但是 start ...