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,所以,接着 ...
随机推荐
- 【To Read】
Maximal Rectangle Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle conta ...
- 【JZOJ4928】【NOIP2017提高组模拟12.18】A
题目描述 数据范围 对于100%的数据,n<=100000,1<=A[i]<=5000 =w= Ans=∏1ai 代码 #include<iostream> #inclu ...
- 【JZOJ4803】【NOIP2016提高A组模拟9.28】求导
题目描述 输入 输出 样例输入 2x^2+3x+1 样例输出 4x+3 数据范围 样例解释 求导的意思: 多项式是由若干个单项式构成的 单项式的一般形式是ax^b,其中ab都是常数,x是自变量 对于单 ...
- WPF/Silverlight深度解决方案:(六)HLSL自定义渲染特效之完美攻略(上)
原文:WPF/Silverlight深度解决方案:(六)HLSL自定义渲染特效之完美攻略(上) Shader Effect种位图特效及2种渲染特效,而Silverlight中仅有这2种渲染特效: Bl ...
- oracle-17113错误
Errors in file /oracle/OraHome1/admin/hncrm/udump/hncrm_ora_24470.trc: ORA-00600: internal error cod ...
- 阿里巴巴资深技术专家无相:我们能从 InteliJ IDEA 中学到什么?
本文来源于阿里巴巴资深技术专家无相在内网的分享,阿里巴巴中间件受权发布. 最近因为工作的关系,要将 Eclipse 的插件升级为 IDEA 插件.升级过程中,对 IDEA 插件做了些学习和研究,希望通 ...
- hdu3549 最大流
#include<stdio.h> #include<string.h> #include<queue> #define MAXN 1010 using names ...
- iOS 万能跳转界面方法 (runtime实用篇一)
http://www.cocoachina.com/ios/20150824/13104.html 作者:汉斯哈哈哈 授权本站转载. 在开发项目中,会有这样变态的需求: 推送:根据服务端推送过来的数据 ...
- 2019-4-7-VisualStudio-解决方案筛选器-slnf-文件
title author date CreateTime categories VisualStudio 解决方案筛选器 slnf 文件 lindexi 2019-04-07 11:34:59 +08 ...
- DLA SQL分析函数:SQL语句审计与分析的利器
1. 简介 Data Lake Analytics(https://www.aliyun.com/product/datalakeanalytics)最新release一组SQL内置函数,用来进行SQ ...