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,所以,接着 ...
随机推荐
- 推荐一个好用的git图形化工具
之前一直用的是SmartGit,虽然贼好用,但是是收费的. 现在看到了一款免费也非常炫酷的工具,忍不住强烈推荐一下GitKraken rebase,cherry pick啥的用挺好用
- span元素和div元素的浮动效果
首先看一段代码: <style> #right {margin: 10px;float:right;color:red;} #left {float:left;color:blue;} & ...
- IO-02. 整数四则运算
本题要求编写程序,计算2个正整数的和.差.积.商并输出.题目保证输入和输出全部在整型范围内. 输入格式: 输入在一行中给出2个正整数A和B. 输出格式: 在4行中按照格式“A 运算符 B = 结果”顺 ...
- 【时光回溯】【JZOJ3566】【GDKOI2014】阶乘
题目描述 输入 第一行有一个正整数T,表示测试数据的组数. 接下来的T行,每行输入两个十进制整数n和base. 输出 对于每组数据,输出一个十进制整数,表示在base进制下,n!结尾的零的个数. 样例 ...
- 笔记:OSAL st 宏学习 do { x } while (__LINE__ == -1)
笔记:OSAL st 宏学习 do { x } while (LINE == -1) #define st(x) do { x } while (__LINE__ == -1) 这段的意思是让代码可以 ...
- 原生js分页
<html> <head> <meta charset='utf-8'> <style type="text/css"> #idDa ...
- MUI - 封装localStorage与plus.storage
MUI - 封装localStorage与plus.storage 2.0版本 在使用plus.storage频繁地存取数据时,可以感觉到明显的卡顿,而且很耗内存, 在切换到localstorage时 ...
- PHP进阶与redis锁限制并发访问功能示例
<?php /** * Redis锁操作类 * Date: 2017-06-30 * Author: fdipzone * Ver: 1.0 * * Func: * public lock 获取 ...
- ios开发――解决UICollectionView的cell间距与设置不符问题
在用UICollectionView展示数据时,有时我们希望将cell的间距调成一个我们想要的值,然后查API可以看到有这么一个属性: - (CGFloat)minimumInteritemSpaci ...
- ACK容器服务发布virtual node addon,快速部署虚拟节点提升集群弹性能力
在上一篇博文中(https://yq.aliyun.com/articles/647119),我们展示了如何手动执行yaml文件给Kubernetes集群添加虚拟节点,然而,手动执行的方式用户体验并不 ...