当前位置:文档之家› C++异常处理机制

C++异常处理机制

C++异常处理机制
C++异常处理机制

C++编译器如何实现异常处理

译者注:本文在网上已经有几个译本,但都不完整,所以我决定自己把它翻译过来。虽然力求信、雅、达,但鉴于这是我的第一次翻译经历,不足之处敬请谅解并指出。

与传统语言相比,C++的一项革命性创新就是它支持异常处理。传统的错误处理方式经常满足不了要求,而异常处理则是一个极好的替代解决方案。它将正常代码和错误处理代码清晰的划分开来,程序变得非常干净并且容易维护。本文讨论了编译器如何实现异常处理。我将假定你已经熟悉异常处理的语法和机制。本文还提供了一个用于VC++的异常处理库,要用库中的处理程序替换掉VC++提供的那个,你只需要调用下面这个函数:

install_my_handler();

之后,程序中的所有异常,从它们被抛出到堆栈展开(stack unwinding),再到调用catch块,最后到程序恢复正常运行,都将由我的异常处理库来管理。

与其它C++特性一样,C++标准并没有规定编译器应该如何来实现异常处理。这意味着每一个编译器的提供商都可以用它们认为恰当的方式来实现它。下面我会描述一下VC++是怎么做的,但即使你使用其它的编译器或操作系统①,本文也应该会是一篇很好的学习材料。VC++的实现方式是以windows系统的结构化异常处理(SEH)②为基础的。

结构化异常处理—概述

在本文的讨论中,我认为异常或者是被明确的抛出的,或者是由于除零溢出、空指针访问等引起的。当它发生时会产生一个中断,接下来控制权就会传递到操作系统的手中。操作系统将调用异常处理程序,检查从异常发生位置开始的函数调用序列,进行堆栈展开和控制权转移。Windows定义了结构“EXCEPTION_REGISTRATION”,使我们能够向操作系统注册自己的异常处理程序。

struct EXCEPTION_REGISTRATION

{

EXCEPTION_REGISTRATION* prev;

DWORD handler;

};

注册时,只需要创建这样一个结构,然后把它的地址放到FS段偏移0的位置上去就行了。下面这句汇编代码演示了这一操作:mov FS:[0], exc_regp

prev字段用于建立一个EXCEPTION_REGISTRATION结构的链表,每次注册新的EXCEPTION_REGISTRATION时,我们都要把原来注册的那个的地址存到prev中。那么,那个异常回调函数长什么样呢?在excpt.h中,windows定义了它的原形:

EXCEPTION_DISPOSITION (*handler)(

_EXCEPTION_RECORD *ExcRecord,

void* EstablisherFrame,

_CONTEXT *ContextRecord,

void* DispatcherContext);

不要管它的参数和返回值,我们先来看一个简单的例子。下面的程序注册了一个异常处理程序,然后通过除以零产生了一个异常。异常处理程序捕获了它,打印了一条消息就完事大吉并退出了。

#include

#include

using std::cout;

using std::endl;

struct EXCEPTION_REGISTRATION

{

EXCEPTION_REGISTRATION* prev;

DWORD handler;

};

EXCEPTION_DISPOSITION myHandler(

_EXCEPTION_RECORD *ExcRecord,

void * EstablisherFrame,

_CONTEXT *ContextRecord,

void * DispatcherContext)

{

cout << "In the exception handler" << endl;

cout << "Just a demo. exiting..." << endl;

exit(0);

return ExceptionContinueExecution; //不会运行到这

}

int g_div = 0;

void bar()

{

//初始化一个EXCEPTION_REGISTRATION结构

EXCEPTION_REGISTRATION reg, *preg = ?

reg.handler = (DWORD)myHandler;

//取得当前异常处理链的“头”

DWORD prev;

_asm

{

mov EAX, FS:[0]

mov prev, EAX

}

reg.prev = (EXCEPTION_REGISTRATION*) prev;

//注册!

_asm

{

mov EAX, preg

mov FS:[0], EAX

}

//产生一个异常

int j = 10 / g_div; //异常,除零溢出

}

int main()

{

bar();

return 0;

}

/*-------输出-------------------

In the exception handler

Just a demo. exiting...

---------------------------------*/

注意EXCEPTION_REGISTRATION必须定义在栈上,并且必须位于比上一个结点更低的内存地址上,Windows对此有严格要求,达不到的话,它就会立刻终止进程。

函数和堆栈

堆栈是用来保存局部对象的连续内存区。更明确的说,每个函数都有一个相关的栈桢(stack frame)来保存它所有的局部对象和表达式计算过程中用到的临时对象,至少理论上是这样的。但现实中,编译器经常会把一些对象放到寄存器中以便能以更快的速度访问。堆栈是一个处理器(CPU)层次的概念,为了操纵它,处理器提供了一些专用的寄存器和指令。

图1是一个典型的堆栈,它示出了函数foo调用bar,bar又调用widget时的情景。请注意堆栈是向下增长的,这意味着新压入的项的地址低于原有项的地址。

通常编译器使用EBP寄存器来指示当前活动的栈桢。本例中,CPU正在运行widget,所以图中的EBP指向了widget的栈桢。编译器在编译时将所有局部对象解析成相对于栈桢指针(EBP)的固定偏移,函数则通过栈桢指针来间接访问局部对象。举个例子,典型的,widget 访问它的局部变量时就是通过访问栈桢指针以下的、有着确定位置的几个字节来实现的,比如说EBP-24。上图中也画出了ESP寄存器,它叫栈指针,指向栈的最后一项。在本例中,ESP 指着widget的栈桢的末尾,这也是下一个栈桢(如果它被创建的话)的开始位置。

处理器支持两种类型的栈操作:压栈(push)和弹栈(pop)。比如,

pop EAX

的作用是从ESP所指的位置读出4字节放到EAX寄存器中,并把ESP加上(记住,栈是向下增长的)4(在32位处理器上);类似的,

push EBP

的作用是把ESP减去4,然后将EBP的值放到ESP指向的位置中去。

编译器编译一个函数时,会在它的开头添加一些代码来为其创建并初始化栈桢,这些代码被称为序言(prologue);同样,它也会在函数的结尾处放上代码来清除栈桢,这些代码叫做尾声(epilogue)。

一般情况下,序言是这样的:

Push EBP ; 把原来的栈桢指针保存到栈上

Mov EBP, ESP ; 激活新的栈桢

Sub ESP, 10 ; 减去一个数字,让ESP指向栈桢的末尾

第一条指令把原来的栈桢指针EBP保存到栈上;第二条指令通过让EBP指向主调函数的EBP 的保存位置来激活被调函数的栈桢;第三条指令把ESP减去了一个数字,这样ESP就指向了当前栈桢的末尾,而这个数字是函数要用到的所有局部对象和临时对象的大小。编译时,编译器知道函数的所有局部对象的类型和“体积”,所以,它能很容易的计算出栈桢的大小。

尾声所做的正好和序言相反,它必须把当前栈桢从栈上清除掉:

Mov ESP, EBP

Pop EBP ; 激活主调函数的栈桢

Ret ; 返回主调函数

它让ESP指向主调函数的栈桢指针的保存位置(也就是被调函数的栈桢指针指向的位置),弹出EBP从而激活主调函数的栈桢,然后返回主调函数。

一旦CPU遇到返回指令,它就要做以下两件事:把返回地址从栈中弹出,然后跳转到那个地址去。返回地址是主调函数执行call指令调用被调函数时自动压栈的。Call指令执行时,会先把紧随在它后面的那条指令的地址(被调函数的返回地址)压入栈中,然后跳转到被调函数的开始位置。图2更详细的描绘了运行时的堆栈。如图所示,主调函数把被调函数的参数也压进了堆栈,所以参数也是栈桢的一部分。函数返回后,主调函数需要移除这些参数,它通过把所有参数的总体积加到ESP上来达到目的,而这个体积可以在编译时知道:

Add ESP, args_size

当然,也可以把参数的总体积写在被调函数的返回指令的后面,让被调函数去移除参数,下面的指令就在返回主调函数前从栈中移去了24个字节:

Ret 24

取决于被调函数的调用约定(call convention),这两种方式每次只能用一个。你还要注意的是每个线程都有自己独立的堆栈。

C++和异常

回忆一下我在第一节中介绍的EXCEPTION_REGISTRATION结构,我们曾用它向操作系统注册了发生异常时要被调用的回调函数。VC++也是这么做的,不过它扩展了这个结构的语义,在它的后面添加了两个新字段:

struct EXCEPTION_REGISTRATION

{

EXCEPTION_REGISTRATION* prev;

DWORD handler;

int id;

DWORD ebp;

};

VC++会为绝大部分函数③添加一个EXCEPTION_REGISTRATION类型的局部变量,它的最后一个字段(ebp)与栈桢指针指向的位置重叠。函数的序言创建这个结构并把它注册给操作系统,尾声则恢复主调函数的EXCEPTION_REGISTRATION。id字段的意义我将在下一节介绍。

VC++编译函数时会为它生成两部分数据:

a)异常回调函数

b)一个包含函数重要信息的数据结构,这些信息包括catch块、这些块的地址和这些块所关心的异常的类型等等。我把这个结构称为funcinfo,有关它的详细讨论也在下一节。

图3是考虑了异常处理之后的运行时堆栈。widget的异常回调函数位于由FS:[0]指向的异常处理链的开始位置(这是由widget的序言设置的)。异常处理程序把widget的funcinfo结构的地址交给函数__CxxFrameHandler,__CxxFrameHandler会检查这个结构看函数中有没有catch 块对当前的异常感兴趣。如果没有的话,它就返回ExceptionContinueSearch给操作系统,于是操作系统会从异常处理链表中取得下一个结点,并调用它的异常处理程序(也就是调用当前函数的那个函数的异常处理程序)。

这一过程将一直进行下去——直到处理程序找到一个能处理当前异常的catch块为止,这时它就不再返回操作系统了。但是在调用catch块之前(由于有funcinfo结构,所以知道catch块的入口,参见图3),必须进行堆栈展开,也就是清理掉当前函数的栈桢下面的所有其他的栈桢。这个操作稍微有点复杂,因为:异常处理程序必须找到异常发生时生存在这些栈桢上的所有局部对象,并依次调用它们的析构函数。后面我将对此进行详细介绍。

异常处理程序把这项工作委托给了各个栈桢自己的异常处理程序。从FS:[0]指向的异常处理链的第一个结点开始,它依次调用每个结点的处理程序,告诉它堆栈正在展开。与之相呼应,这些处理程序会调用每个局部对象的析构函数,然后返回。此过程一直进行到与异常处理程序自

身相对应的那个结点为止。

由于catch块是函数的一部分,所以它使用的也是函数的栈桢。因此,在调用catch块之前,异常处理程序必须激活它所隶属的函数的栈桢。

其次,每个catch块都只接受一个参数,其类型是它希望捕获的异常的类型。异常处理程序必须把异常对象本身或者是异常对象的引用拷贝到catch块的栈桢上,编译器在funcinfo中记录了相关信息,处理程序根据这些信息就能知道到哪去拷贝异常对象了。

拷贝完异常并激活栈桢后,处理程序将调用catch块。而catch块将把控制权下一步要转移到的地址返回来。请注意:虽然这时堆栈已经展开,栈桢也都被清除了,但它们占据的内存空间并没有被覆盖,所有的数据都还好好的待在栈上。这是因为异常处理程序仍在执行,象其他函数一样,它也需要栈来存放自己的局部对象,而其栈桢就位于发生异常的那个函数的栈桢的下面。catch块返回以后,异常处理程序需要“杀掉”异常对象。此后,它让ESP指向目标函数(控制权要转移到的那个函数)的栈桢的末尾——这样就把(包括它自己的在内的)所有栈桢都删除了,然后再跳转到catch块返回的那个地址去,就胜利的完成整个异常处理任务了。但它怎么知道目标函数的栈桢末尾在哪呢?事实上它没法知道,所以编译器把这个地址保存到了栈桢上(由前言来完成),如图3所示,栈桢指针EBP下面第16个字节就是。

当然,catch块也可能抛出新异常,或者是将原来的异常重新抛出。处理程序必须对此有所准备。如果是抛出新异常,它必须杀掉原来的那个;而如果是重新抛出原来的异常,它必须能继续传播(propagate)这个异常。

这里我要特别强调一点:由于每个线程有自己独立的堆栈,所以每个线程也都有自己独立的、由FS:[0]指向的EXCEPTION_REGISTRATION链。

C++和异常—2

图4是funcinfo的布局,注意这里的字段名可能与VC++编译器实际使用的不完全一致,而且我也只给出了和我们的讨论相关的字段。堆栈展开表(unwind table)的结构留到下节再讨论。

异常处理程序在函数中查找catch块时,它首先要判断异常发生的位置是否在当前函数(发生异常的那个函数)的一个try块中。是则查找与此try块相关的catch块表,否则直接返回。

先来看看它怎样找try块。编译时,编译器给每个try块都分配了start id和end id。通过funcinfo结构,异常处理程序可以访问这两个id,见图4。编译器为函数中的每个try块都生成了相关的数据结构。

上一节中,我说过VC++给EXCEPTION_REGISTRATION结构加上了一个id字段。回忆一下图3,这个结构位于函数的栈桢上。异常发生时,处理程序读出这个值,看它是否在try块的两个id确定的区间[start id,end id]中。是的话,异常就发生在这个try块中;否则继续查看try 块表中的下一个try块。

谁负责更新id的值,它的值又应该是什么呢?原来,编译器会在函数的多个位置安插代码来更新id的值,以反应程序的实时运行状态。比如说,编译器会在进入try块的地方加上一条语句,把try块的start id写到栈桢上。

找到try块后,处理程序就遍历与其关联的catch块表,看是否有对当前异常感兴趣的catch 块。在try块发生嵌套时,异常将既源于内层try块,也源于外层try块。这种情况下,处理程序应该按先内后外的顺序查找catch块。但它其实没必要关心这些,因为,在try块表中,

VC++总是把内层try块放在外层try块的前面。

异常处理程序还有一个难题就是“如何根据catch块的相关数据结构判断这个catch块是否愿意处理当前异常”。这是通过比较异常的类型和catch块的参数的类型来完成的。例如下面这个程序:

void foo()

{

try

{

throw E();

}

catch(H)

{

//.

}

}

如果H和E的类型完全相同的话,catch块就要捕获这个异常。这意味着处理程序必须在运行时进行类型比较,对C等语言来说,这是不可能的,因为它们无法在运行时得到对象的类型。C++则不同,它有了运行时类型识别(runtime type identification,RTTI),并提供了运行时类型比较的标准方法。C++在标准头文件中定义了type_info类,它能在运行时代表一个类型。catch块数据结构的第二个字段(ptype_info,见图4)是一个指向type_info结构的指针,它在运行时就代表catch块的参数类型。type_info也重载了==运算符,能够指出两种类型是否完全相同。这样,异常处理程序只要比较(调用==运算符)catch块参数的type_info(可以通过catch块的相关数据结构来访问)和异常的type_info是否相同,就能知道catch块是不是愿意捕获当前异常了。catch块的参数类型可以通过funcinfo结构得到,但异常的type_info从哪来

呢?当编译器碰到 throw E();这条语句时,它会为异常生成一个excpt_info结构,如图5所示。还是要提醒你注意这里用的名字可能与VC++使用的不一致,而且仍然只有与我们的讨论相关的字段。从图中可以看出,异常的type_info可以通过excpt_info结构得到。由于异常处理程序需要拷贝异常对象(在调用catch块之前),也需要消除掉它(在调用catch块之后),所以编译器在这个结构中同时提供了异常的拷贝构造函数、大小和析构函数的信息。

在catch块的参数是基类,而异常是派生类时,异常处理程序也应该调用catch块。然而,这种情况下,比较它们的type_info绝对是不相等,因为它们本来就不是相同的类型。而且,

type_info类也没有提供任何其他函数或运算符来指出一个类是另一个类的基类。但异常处理程序还必须得去调用catch块!为了解决这个问题,编译器只能为处理程序提供更多的信息:如果异常是派生类,那么etypeinfo_table(通过excpt_info访问)将包含多个指向etype_info(扩展了type_info,这个名字是我启的)的指针,它们分别指向了各个基类的etype_info。这样,处理程序就可以把catch块的参数和所有这些type_info比较,只要有一个相同,就调用catch 块。

在结束这一部分之前,还有最后一个问题:异常处理程序是怎么知道异常和excpt_info结构的?下面我就要回答这个问题。

VC++会把throw语句翻译成下面的样子:

//throw E(); //编译器会为E生成excpt_info结构

E e = E(); //在栈上创建异常

_CxxThrowException(&e, E_EXCPT_INFO_ADDR);

__CxxThrowException会把控制权连带它的两个参数都交给操作系统(控制权转移是通过软件中断实现的,请参见RaiseException)。而操作系统,在为调用异常回调函数做准备时,会把这两个参数打包到一个_EXCEPTION_RECORD结构中。接着,它从

EXCEPTION_REGISTRATION链表的头结点(由FS:[0]指向)开始,依次调用各节点的异常处理程序。而且,指向当前EXCEPTION_REGISTRATION结构的指针也会作为异常处理程序的第二个参数出现。前面已经说过,VC++中的每个函数都在栈上创建并注册了EXCEPTION_REGISTRATION结构。所以传递这个参数可以让处理程序知道很多重要信息,比如说:EXCEPTION_REGISTRATION的id字段(用于查找catch块)、函数的栈桢(用于清理栈桢)和EXCEPTION_REGISTRATION结点在异常链表中的位置(用于堆栈展开)等。第一个参数是指向_EXCEPTION_RECORD结构的指针,通过它可以找到异常和它的

excpt_info结构。下面是excpt.h中定义的异常回调函数的原型:

EXCEPTION_DISPOSITION (*handler)(

_EXCEPTION_RECORD* ExcRecord,

void* EstablisherFrame,

_CONTEXT *ContextRecord,

void* DispatcherContext);

后两个参数和我们的讨论关系不大。函数的返回值是一个枚举类型(也在excpt.h中定义),我前面已经说过,如果处理程序找不到catch块,它就会向系统返回ExceptionContinueSearch,对本文而言,我们只要知道这一个返回值就行了。

_EXCEPTION_RECORD结构是在winnt.h中定义的:

struct _EXCEPTION_RECORD

{

DWORD ExceptionCode;

DWORD ExceptionFlags;

_EXCEPTION_RECORD* ExcRecord;

PVOID ExceptionAddress;

DWORD NumberParameters;

DWORD ExceptionInformation[15];

}EXCEPTION_RECORD;

ExceptionInformation数组中元素的个数和类型取决于ExceptionCode字段。如果是C++异常(异常代码是0xe06d7363,源于throw语句),那么数组中将包含指向异常和excpt_info结构

的指针;如果是其他异常,那数组中基本上就不会有什么内容,这些异常包括除零溢出、访问违例等,你可以在winnt.h中找到它们的异常代码。

ExceptionFlags字段用于告诉异常处理程序应该采取什么操作。如果它是EH_UNWINDING (见Except.inc),那是说堆栈正在展开,这时,处理程序要清理栈桢,然后返回。否则处理程序应该在函数中查找catch块并调用它。清理栈桢意味着必须找到异常发生时生存在栈桢上的所有局部对象,并调用其析构函数,下一节我们将就此进行详细讨论。

清理栈桢

C++标准明确指出:堆栈展开工作必须调用异常发生时所有生存的局部对象的析构函数。如下面的代码:

int g_i = 0;

void foo()

{

T o1, o2;

{

T o3;

}

10/g_i; //这里会发生异常

T o4;

//...

}

foo有o1、o2、o3、o4四个局部对象,但异常发生时,o3已经“死亡”,o4还未“出生”,所以异常处理程序应该只调用o1和o2的析构函数。

前面已经说过,编译器会在函数的很多地方安插代码来记录当前的运行状态。实际上,编译器在函数中设置了一些关键区域,并为它们分配了id,进入关键区域时要记录它的id,退出时恢复前一个id。try块就是一个例子,其id就是start id。所以,在try块的入口,编译器会把它的start id记到栈桢上去。局部对象从创建到销毁也确定了一个关键区域,或者,换句话说,编译器给每个局部对象分配了唯一的id,例如下面的程序:

void foo()

{

T t1;

//.

}

编译器会在t1的定义后面(也就是t1创建以后),把它的id写到栈桢上:

void foo()

{

T t1;

_id = t1_id; //编译器插入的语句

//.

}

上面的_id是编译器偷偷创建的局部变量,它的位置与EXCEPTION_REGISTRATION的id字段重叠。类似的,在调用对象的析构函数前,编译器会恢复前一个关键区域的id。

清理栈桢时,异常处理程序读出id的值(通过EXCEPTION_REGISTRATION结构的id字段或栈桢指针EBP下面的4个字节来访问)。这个id可以表明,函数在运行到与它相关联的那个点之前没有发生异常。所有在这一点之前定义的对象都已初始化,应该调用这些对象中的一部分或全部对象的析构函数。请注意某些对象是属于子块(如前面代码中的o3)的,发生异常时可能已经销毁了,不应该调用它们的析构函数。

编译器还为函数生成了另一个数据结构——堆栈展开表(unwindtable,我启的名字),它是一个unwind结构的数组,可通过funcinfo来访问,如图4所示。函数的每个关键区域都有一个unwind结构,这些结构在展开表中出现的次序和它们所对应的区域在函数中的出现次序完全相同。一般unwind结构也会关联一个对象(别忘了,每个对象的定义都开辟了关键区域,并有id与其对应),它里面有如何销毁这个对象的信息。每当编译器碰到对象定义,它就生成一小段代码,这段代码知道对象在栈桢上的地址(就是它相对于栈桢指针的偏移),并能销毁它。unwind结构中有一个字段用于保存这段代码的入口地址:

typedef void (*CLEANUP_FUNC)();

struct unwind

{

int prev;

CLEANUP_FUNC cf;

};

try块对应的unwind结构的cf字段是空值NULL,因为没有与它对应的对象,所以也没有东西需要它去销毁。通过prev字段,这些unwind结构也形成了一个链表。异常处理程序清理栈桢时,会读取当前的id值,以它为索引取得展开表中对应的项,并调用其第二个字段指向的清理代码,这样,那个与之关联的对象就被销毁了。然后,处理程序将以当前unwind结构的prev字段为索引,继续在展开表中找下一个unwind结构,调用其清理代码。这一过程将一直重复,直到链表的结尾(prev的值是-1)。图6画出了本节开始时提到的那段代码的堆栈展开表。

现在把new运算符也加进来,对于下面的代码:

T* p = new T();

系统会首先为T分配内存,然后调用它的构造函数。所以,如果构造函数抛出了异常,系统就必须释放这些内存。因此,动态创建那些拥有“有为的构造函数”的类型时,VC++也为new运算符分配了id,并且堆栈展开表中也有与其对应的项,其清理代码将释放分配的内存空间。调用构造函数前,编译器把new运算符的id存到EXCEPTION_REGISTRATION结构中,构造函数顺利返回后,它再把id恢复成原来的值。

更进一步说,构造函数抛出异常时,对象可能刚刚构造了一部分,如果它有子成员对象或子基类对象,并且发生异常时它们中的一部分已经构造完成的话,就必须调用这些对象的析构函数。和普通函数一样,编译器也给构造函数生成了相关的数据来帮助完成这个任务。

展开堆栈时,异常处理程序调用的是用户定义的析构函数,这一点你必须注意,因为它也有可能抛出异常!C++标准规定堆栈展开过程中,析构函数不能抛出异常,否则系统将调用std::terminate。

实现

本节我们讨论其他三个有待详细解释的问题:

a)如何安装异常处理程序

b)catch块重新抛出异常或抛出新异常时应该如何处理

c)如何对所有线程提供异常处理支持

随同本文,有一个演示项目,查看其中的readme.txt文件可以得到一些编译方面的帮助①。

第一项任务是安装异常处理程序,也就是把VC++的处理程序替换掉。从前面的讨论中,我们已经清楚地知道__CxxFrameHandler函数是VC++所有异常处理工作的入口。编译器为每个函数都生成一段代码,它们在发生异常时被调用,把相应的funcinfo结构的指针交给

__CxxFrameHandler。

install_my_handler()函数会改写__CxxFrameHandler的入口处的代码,让程序跳转到

my_exc_handler()函数。不过,__CxxFrameHandler位于只读的内存页,对它的任何写操作都会导致访问违例,所以必须首先用VirtualProtectEx把该内存页的保护方式改成可读写,等改写完毕后,再改回只读。写入的数据是一个jmp_instr结构。

//install_my_handler.cpp

#include

#include "install_my_handler.h"

//C++默认的异常处理程序

extern "C"

EXCEPTION_DISPOSITION __CxxFrameHandler(

struct _EXCEPTION_RECORD* ExceptionRecord,

void* EstablisherFrame,

struct _CONTEXT* ContextRecord,

void* DispatcherContext

);

namespace

{

char cpp_handler_instructions[5];

bool saved_handler_instructions = false;

}

namespace my_handler

{

//我的异常处理程序 EXCEPTION_DISPOSITION

my_exc_handler(

struct _EXCEPTION_RECORD *ExceptionRecord,

void * EstablisherFrame,

struct _CONTEXT *ContextRecord,

void * DispatcherContext

) throw();

#pragma pack(push, 1)

struct jmp_instr

{

unsigned char jmp;

DWORD offset;

};

#pragma pack(pop)

bool WriteMemory(void* loc, void* buffer, int size)

{

HANDLE hProcess = GetCurrentProcess();

//把包含内存范围[loc,loc+size]的页面的保护方式改成可读写

DWORD old_protection;

BOOL ret = VirtualProtectEx(hProcess, loc, size, PAGE_READWRITE, &old_protection); if(ret == FALSE)

return false;

ret = WriteProcessMemory(hProcess, loc, buffer, size, NULL);

//恢复原来的保护方式

DWORD o2;

VirtualProtectEx(hProcess, loc, size, old_protection, &o2);

return (ret == TRUE);

}

bool ReadMemory(void* loc, void* buffer, DWORD size)

{

HANDLE hProcess = GetCurrentProcess();

DWORD bytes_read = 0;

BOOL ret = ReadProcessMemory(hProcess, loc, buffer, size, &bytes_read);

return (ret == TRUE && bytes_read == size);

}

bool install_my_handler()

{

void* my_hdlr = my_exc_handler; void* cpp_hdlr = __CxxFrameHandler;

jmp_instr jmp_my_hdlr;

jmp_my_hdlr.jmp = 0xE9;

//从__CxxFrameHandler+5开始计算偏移,因为jmp指令长5字节

jmp_my_hdlr.offset = reinterpret_cast(my_hdlr) - (reinterpret_cast(cpp_hdlr) + 5);

if(!saved_handler_instructions)

{

if(!ReadMemory(cpp_hdlr, cpp_handler_instructions, sizeof(cpp_handler_instructions))) return false;

saved_handler_instructions = true;

}

return WriteMemory(cpp_hdlr, &jmp_my_hdlr, sizeof(jmp_my_hdlr));

}

bool restore_cpp_handler()

{

if(!saved_handler_instructions)

return false;

else

{

void* loc = __CxxFrameHandler;

return WriteMemory(loc, cpp_handler_instructions, sizeof(cpp_handler_instructions)); }

}

}

中断异常处理流程

计算机体系结构中,异常或者中断是处理系统中突发事件的一种机制,几乎所有的处理器都提供这种机制。异常主要是从处理器被动接受的角度出发的一种描述,指意外操作引起的异常。而中断则带有向处理器主动申请的意味。但这两种情况具有一定的共性,都是请求处理器打断正常的程序执行流程,进入特定程序的一种机制。若无特别说明,对“异常”和“中断”都不作严格的区分。本文结合经过实际验证的代码对ARM9中断处理流程进行分析,并设计出基于S3C2410芯片的外部中断处理程序。 1.异常中断响应和返回 系统运行时,异常可能会随时发生。当一个异常出现以后,ARM微处理器会执行以下几步操作: 1) 将下一条指令的地址存入相应连接寄存器LR,以便程序在处理异常返回时能从正确的位置重新开始执行。 2)将CPSR复制到相应的SPSR中。 3)根据异常类型,强制设置CPSR的运行模式位。 4) 强制PC从相关的异常向量地址取下一条指令执行,从而跳转到相应的异常处理程序处。 这些工作是由ARM内核完成的,不需要用户程序参与。异常处理完毕之后,ARM微处理器会执行以下几步操作从异常返回: 1)将连接寄存器LR的值减去相应的偏移量后送到PC中。 2)将SPSR复制回CPSR中。 3) 若在进入异常处理时设置了中断禁止位,要在此清除。 这些工作必须由用户在中断处理函数中实现。为保证在ARM处理器发生异常时不至于处于未知状态,在应用程序的设计中,首先要进行异常处理。采用的方式是在异常向量表中的特定位置放置一条跳转指令,跳转到异常处理程序。当ARM处理器发生异常时,程序计数器PC会被强制设置为对应的异常向量,从而跳转到异常处理程序。当异常处理完成以后,返回到主程序继续执行。可以认为应用程序总是从复位异常处理程序开始执行的,因此复位异常处理程序不需要返回。 2.异常处理程序设计 2.1 异常响应流程

程序设计异常处理机制

异常处理是程序设计中一个非常重要的方面,也是程序设计的一大难点,从C开始,你也许已经知道如何用if...else...来控制异常了,也许是自发的,然而这种控制异常痛苦,同一个异常或者错误如果多个地方出现,那么你每个地方都要做相同处理,感觉相当的麻烦!Java 语言在设计的当初就考虑到这些问题,提出异常处理的框架的方案,所有的异常都可以用一个类型来表示,不同类型的异常对应不同的子类异常(这里的异常包括错误概念),定义异常处理的规范,在1.4版本以后增加了异常链机制,从而便于跟踪异常!这是Java语言设计者的高明之处,也是Java语言中的一个难点,下面是我对Java异常知识的一个总结,也算是资源回收一下。 一、Java异常的基础知识 异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。比如说,你的代码少了一个分号,那么运行出来结果是提示是错误https://www.doczj.com/doc/cb468481.html,ng.Error;如果你用System.out.println(11/0),那么你是因为你用0做了除数,会抛出https://www.doczj.com/doc/cb468481.html,ng.ArithmeticException的异常。 有些异常需要做处理,有些则不需要捕获处理,后面会详细讲到。 天有不测风云,人有旦夕祸福,Java的程序代码也如此。在编程过程中,首先应当尽可能去避免错误和异常发生,对于不可避免、不可预测的情况则在考虑异常发生时如何处理。Java中的异常用对象来表示。Java对异常的处理是按异常分类处理的,不同异常有不同的分类,每种异常都对应一个类型(class),每个异常都对应一个异常(类的)对象。 异常类从哪里来?有两个来源,一是Java语言本身定义的一些基本异常类型,二是用户通过继承Exception类或者其子类自己定义的异常。Exception 类及其子类是Throwable的一种形式,它指出了合理的应用程序想要捕获的条件。 异常的对象从哪里来呢?有两个来源,一是Java运行时环境自动抛出系统生成的异常,而不管你是否愿意捕获和处理,它总要被抛出!比如除数为0的异常。二是程序员自己抛出的异常,这个异常可以是程序员自己定义的,也可以是Java语言中定义的,用throw 关键字抛出异常,这种异常常用来向调用者汇报异常的一些信息。 异常是针对方法来说的,抛出、声明抛出、捕获和处理异常都是在方法中进行的。 Java异常处理通过5个关键字try、catch、throw、throws、finally进行管理。基本过程是用try语句块包住要监视的语句,如果在try语句块内出现异常,则异常会被抛出,你的代码在catch语句块中可以捕获到这个异常并做处理;还有以部分系统生成的异常在Java运行时自动抛出。你也可以通过throws关键字在方法上声明该方法要抛出异常,然后在方法内部通过throw抛出异常对象。finally语句块会在方法执行return之前执行,一般结构如下: try{ 程序代码 }catch(异常类型1 异常的变量名1){ 程序代码 }catch(异常类型2 异常的变量名2){ 程序代码 }finally{ 程序代码 } catch语句可以有多个,用来匹配多个异常,匹配上多个中一个后,执行catch语句块时候仅仅执行匹配上的异常。catch的类型是Java语言中定义的或者程序员自己定义的,表示代

异常处理机制

异常的基本概念 异常是导致程序终止运行的一种指令流,如果不对异常进行正确的处理,则可能导致程序的中断执行,造成不必要的损失。 在没有异常处理的语言中如果要回避异常,就必须使用大量的判断语句,配合所想到的错误状况来捕捉程序中所有可能发生的错误。 Java异常处理机制具有易于使用、可自行定义异常类、处理抛出的异常同时又不会降低程序运行的速度等优点。因而在java程序设计时应充分地利用java的异常处理机制,以增进程序的稳定性及效率。 当程序中加入了异常处理代码,所以当有异常发生后,整个程序并不会因为异常的产生而中断执行。而是在catch中处理完毕之后,程序正常的结束。 在整个java异常的结构中,实际上有两个最常用的类,分别为Exception和Error 这两个类全都是Throwable的子类。 Exception:一般表示的是程序中出现的问题,可以直接使用try……catch处理。 Error:一般值JVM错误,程序中无法处理。 Java异常处理机制。 在整个java的异常处理中,实际上也是按照面向对象的方式进行处理,处理的步骤如下: 1)一旦产生异常,则首先会产生一个异常类的实例化对象。 2)在try语句中对此异常对象进行捕捉。 3)产生的异常对象与catch语句中的各个异常类型进行匹配,如果匹配成功则执行catch语句中的代码。 异常处理 在定义一个方法时可以使用throws关键字声明,表示此方法不处理异常,而交给方法的调用处进行处理,在方法调用处不管是否有问题,都要使用try……catch块进行异常的捕获与处理。 如果在主方法中使用throws关键字,则程序出现问题后肯定交由jvm处理,将导致程序中断。 与throws关键字不同的是,throw关键字人为的抛出一个异常,抛出时直接抛出异常类的实例化对象即可。 Exception在程序中必须使用try……catch进行处理。RuntimeException可以不使用try……catch进行处理,但是如果有异常产生,则异常将由JVM进行处理。(建议RuntimeException的子类也使用try……catch进行处理,否则产生的异常交给jvm处理会导致程序中断。) 继承关系: Exception》RuntimeException》lllegalArgumentException》NumberFormatException; 异常类必须继承于Exception 建议:继承Exception一般要添加全部父类型一样的构造器! class NameOrPwdException extends Exception { public NameOrPwdException() {

设备故障应急处理预案完整版本

设备故障应急处理预案 1 设备维修程序 1.1 设备需要维修,使用部门如实填报报修单,部门负责人签字后送工程部。 1.2 急需维修时,使用部门也可直接电话通知工程部。 1.3 工程部接报修单或电话后应在5分钟内及时派工,维修人员到达现场后,凭报修单进行维修。特殊情况可先维修,然后补报修单。 1.4 修复后使用部门应在报修单上签字认可。 1.5 无法修复时,维修工应将无法修复的原因写在报修单上,签字并送工程部负责人手中 1.6 工程部负责人根据情况,属零配件问题的,可按程序填报申报表;属技术原因无法修复的,在2-4小时内报主管总经理。 1.7 关于维修时现场维修应注意的礼仪,按《维修服务规范》执行。 2 公共部位巡查检修对于几个部门共同使用且较难界定由谁负责的公共部位设施设备,工程部派人进行巡查检修。每周一次,做好记录,一般故障由巡查员现场修复,重大故障由巡查员汇报当班负责人后安排检修。 当设备发生故障时 1、先停用故障设备,起动备用设备,防止故障设备的故障扩大及防止影响服务区域。 2、自动化的设备失灵后,即安排人员进行手动操作确保服务区域正常,与此同时再积极组织抢修。 3、降低设备的负荷,减少服务范围,尽力保证不影响对客服务。 4、如空调设备发生问题时,应严格控制新风量,确保空调区域的温度。 停电 一、事故停电 1、事故停电是指外供电线路发生事故造成停电,这种停电分大面积停电无法恢复和瞬间闪断两种。 2、事故停电由于属于突发事件,所以情况一般都非常紧急需要各部门协调工作。 3、配电值班人员发现停电后要第一时间询问供电部门停电原因,及时通知大堂副理、夜间要通知值班经理、部门经理、及酒店各相关值班岗位。

异常情况处理制度及流程

山西煤炭运销集团 蒲县昊锦塬煤业有限公司异常情况处理制度为认真贯彻落实国家、省、市关于集中开展安全生产大检查的工作安排要求,加强我矿信息监控系统管理水平,做好矿井生产过程中井下环境参数的有效监控,保障矿井安全生产,加强煤矿安全生产管理水平及抗灾能力,特制定本矿异常情况处理制度如下: 一、值班人员按《中心岗位责任制》规定,浏览查询煤矿安全信息,发现异常情况及时处理,并认真填写《异常情况报告处理表》,传真至县监控中心。 二、监控室值班人员发现系统发出异常报警后,值班人员必须立即通知监控室主任、分管领导,同时立即通知矿井调度部门,由监控室主任或分管领导组织相关人员对本次异常报警进行原因分析,并按规定程序及时报上一级网络中心。处理结果应记录备案。调度值班人员接到报警、断电信息后,应立即向矿值班领导汇报,矿值班领导按规定指挥现场人员停止工作,断电时撤出人员。处理过程应记录备案。当系统显示井下某一区域瓦斯超限并有可能波及其他区域时,矿井有关人员应按瓦斯事故应急预案手动遥控切断瓦斯可能波及区域的电源。值班人员接到网络中心发出的报警处理指令后,要立即处理落实,并将处理结果向网络中心反馈。 当工作面瓦斯浓度达到报警浓度时,值班人员应立即通知矿值班领导及监控室主任,并填写异常情况处理报告表传真上报至

县监控中心

;由分管领导或监控室主任安排相关人员进行原因分析,按照瓦斯超限分析原则:①按人工检测值与甲烷传感器对比分析; ②按报警地点的历史曲线对比分析;③按报警地点上风侧检测值对比分析。根据分析结果立即将处理措施下达至矿调度中心按处理措施严格执行。报警期间要采取安全措施,报警消除后将报警的起止时间、分析报告、采取措施和处理结果上报县监控室并存档备案。 三、当煤矿通讯中断、无数据显示时,值班人员要通过传真(或电话)向县监控中心报告,并查明原因,恢复通讯。情况紧急的,由值班人员立即向矿领导汇报,对因故造成通讯中断未及时上报的,要通过电话联系移动公司或长途线务局进行抢修。

如何使用异常处理机制

如何使用异常处理机制 《PHP核心技术与最佳实践》第1章面向对象思想的核心概念,本章将就面向对象一些概念展开讨论,其中重点讨论PHP特色的面向对象的风格和语法,并通过相互借鉴和对比,使读者认识PHP自身的特点,尤其是和其他语言中不同的地方。本节为大家介绍如何使用异常处理机制。 1.6.1 如何使用异常处理机制(1) 异常的思想最早可以追溯到20世纪60年代,其在C++、Java中发扬光大,PHP则部分借鉴了这两种语言的异常处理机制。 PHP里的异常,是程序运行中不符合预期的情况及与正常流程不同的状况。一种不正常的情况,就是按照正常逻辑不该出错,但仍然出错的情况,这属于逻辑和业务流程的一种中断,而不是语法错误。PHP里的错误则属于自身问题,是一种非法语法或者环境问题导致的、让编译器无法通过检查甚至无法运行的情况。 在各种语言里,异常(exception)和错误(error)的概念是不一样的。在PHP里,遇到任何自身错误都会触发一个错误,而不是抛出异常(对于一些情况,会同时抛出异常和错误)。PHP一旦遇到非正常代码,通常都会触发错误,而不是抛出异常。在这个意义上,如果想使用异常处理不可预料的问题,是办不到的。比如,想在文件不存在且数据库连接打不开时触发异常,是不可行的。这在PHP里把它作为错误抛出,而不会作为异常自动捕获。 以经典的除零问题为例,如代码清单1-16所示。 代码清单1-16 exception.php 1.// exception.php 2.getMessage(); 9.$a=-1; 10.}

护理不良事件管理详解

非惩罚性护理不良事件报告制度及激励机制 一、不良事件的定义 是指在护理过程中发生的、不在计划内的跌倒、坠床、压疮、用药错误、走失、误吸或窒息、烫伤及其他与患者安全相关的非正常的护理意外事件。 二、不良事件报告的意义 通过报告不良事件,及时发现潜在的不安全因素,可有效避免护理差错与纠纷的发生,保障病人安全,不良事件的全面报告,有利于发现医院安全系统存在的不足,提高医院系统安全水平,促进医院及时发现安全事故隐患,不断提高对错误的识别能力,不良事件报告后的信息共存,可以使相关人员从他人的过失中吸取经验教训,以免重蹈覆辙。 三、护理不良事件的范围 1、患者在住院期间发生压疮、坠床、跌倒、导管滑脱、用药失误、走失、误吸或窒息、烫伤及其他与患者安全相关的护理意外。 2、因护理操作失误导致患者出现严重并发症、住院时间延长或住院费用增加等。 3、严重药物不良反应或输血不良反应。 4、严重院内感染。 四、不良事件报告原则 非惩罚性、主动性报告的原则:护理部鼓励护理人员主动、自愿报告不良事件,包括本人的或本科室的,也可报告其他人或其他科室的,可以实名报告,也可匿名报告,对主动报告的科室和个人的有关信息,护理部将严格保密。 五、上报内容 包括患者一般资料,不良事件发生的时间地点、不良事件项目分类、发生的主要原因、采取的措施、患者损害的严重程度及后果和改进措施。上报形式以个人或科室为上报单位。 六、上报形式 1、口头报告:发生严重不良事件时,护理人员应立即向护士长、科主任、总值班、护理部口头报告事件情况。 2、书面报告:护理人员书面填写《护理不良事件报告单》。 3、网络报告:护理人员登录内网,填写《护理不良事件报告单》电子表格,

[重点]设备异常处理流程及规定

[重点]设备异常处理流程及规定 设备异常处理流程 序流程图责任人表单作业内容号 班组长/线长不能处生产异常出现时,生产部门/设备生产异常理或异常会导致停产时间超过30分钟 1 相关部门/ 时,应立即上报,或开出《生产异常发现者报告单》进行处理。 生产部负责人接到报告后应在10分钟生产部门/内赶赴现场;必要时可同时通知相关相关人员 2 相关部门/ 部门负责人,相关部门负责人接到通赶赴现场负责人知后应在10分钟内赶到现场( 相关部门负责人到达现场后立即对异相关部门异常分析 3 常进行分析,若部门负责人不能到场负责人应在10分钟内派人到达现场( 如不能立即处理应作出是否停产的意确定是总经办/总4 见,并注明预计恢复生产的时间(停否停产经理产应由总经理批准( 相关部门负责人针对问题应在30分钟制定应急相关部门生产异常 5 内制定出应急处理措施,制定措施时处理措施负责人报告单应尽可能地降低影响生产部门生产异常生产部门按应急措施进行生产按照处理6 负责人报告单调整生产措施生产 生产部/品 质部 NG 应急措施的有效性由生产部与品质部生产异常责任人措施7 共同验证,如验证不符合则重新制定报告单验证相关措施( YES 验证结果符合生产及品质相关要求,生产部负责恢复正8 可以在恢复生产后由品质部和生产部人常生产对异常进行跟进确认(

相关责任部生产恢复正常后相关部门应对问题的生产异常 9 制定长期门深层次的原因加以分析,并在两个工报告单预防措施负责人作日内制定出长期预防措施( 生产部生产异常生产部应协同品质部对责任部门的长10 负责人报告单期预防措施执行结果进行跟踪预防措施跟踪 异常处理规定 1(目的 为了更好的规范和完善公司生产异常处理作业,使生产问题发生后,各部门人员迅速、有效的处理,减免停工时间,提高生产效率,特制定本流程。 2(适用范围 适用于公司所有生产异常的处理。 3(职责 3(1 生产部门负责生产异常的反馈和处理措施验证。 3(2 品质部负责品质异常的处理及验证。 3(3 设备组负责设备异常的处理。 3(4 计控部负责物料异常的处理。 3(5 技术部负责技术、关键工序设备、工装模具、工艺异常的处理。 4(作业规范 4.1 生产异常反馈 4.1.1 当生产发生异常或有出现异常的趋势时,生产部发现人员和现场管理人员(如班组长)应即时给予分析,并主动积极寻求解决方法,包括与相关人员联系,如能及时解决则不在本流程规定内。

设备故障处理流程文档

设备故障得应急 预案及流程 (2016年) 急救仪器设备出现意外故障处理流程 ↓ ↓ ↓ ↓ ↓

使用呼吸机过程中突遇故障(断电) 应急预案及程序 【应急预案】 (一)值班护士应熟知本病房,本班次使用呼吸机病人得病情。住院患者使用呼吸机过程中,如果突然遇到意外停电,跳闸等紧急情况时,医护人员应采取补救措施,以保护病人使用呼吸机得安全. (二)部分呼吸机本身带有蓄电池,在平时应定期充电,使蓄电池始终处于饱与状态,以保证在出现突发情况时能够正常运行、护理人员应定期观察呼吸机蓄电池充电情况、呼吸机能否正常工作及病人生命体征有无变化。 (三)呼吸机不能正常工作时,护士应立即停止应用呼吸机,迅速将简易呼吸器与患者呼吸道相连,用人工呼吸得方法调整患者呼吸;如果病人自主呼吸良好,应给予鼻导管吸氧;严密观察病人得呼吸、面色、意识等情况。 (四)突然断电时,护士应携带简易呼吸器到病人床前,同时通知值班医生,观察患者面色、呼吸、意识及呼吸机工作情况。 (五)立即与有关部门联系:总务科、医院办公室、医务办、护理部、医院总值班等,迅速采取各种措施,尽快恢复供电。 (六)护理人员应遵医嘱给予病人药物治疗。 (七)停电期间,本病区医生、护士不得离开病人,以便随时处理紧急情况。

(八)遵医嘱根据病人情况调整呼吸机参数。来电后,重新将呼吸机与病人呼吸道连接. (九)护理人员将停电经过及病人生命体征准确记录于护理记录单上。 【程序】 突然断电—-使用简易呼吸器——通知值班医生——调整病人呼吸-—观察病情变化-—立即联系有关部门——尽快恢复通电-—随时处理紧急情况—-遵医嘱给药——来电后重新调整应用呼吸机-—准确记录 心电监护仪故障应急预案及处理流程 【应急预案】 1、心电监护仪使用中出现意外停电、故障,首先检查电源线路连接就是否正确,接头就是否松动。 2、评估患者电极片安置部位就是否正确,有无松动。 3、采取以上措施后心电监护仪仍不能正常工作,立即拆下故障心电监护仪,启用备用心电监护仪. 4、严密观察患者得生命体征及病情变化,并向患者及家属做好解释工作. 5、悬挂“仪器故障牌”标识。 6、立即通知仪器维修人员,并报告护士长,作好记录交接,节假日或夜间备用心电监护仪不能满足需要时报告护理部值班人员.

安全生产异常信息报告、收集、分析处理制度(试行)

*********矿井 安全生产异常信息报告、收集、分析处置制度(试行) 第一章总则 第一条为规范矿井安全生产异常信息管理,建立安全生产异常信息报告、收集和分析处置的工作机制,实现安全管理关口前移、超前预警、超前处理,真正做到将事故消灭在萌芽状态,结合矿井现状,特制定本制度。 第二章安全生产异常信息界定 第二条本制度所称的安全生产异常信息,是指事故发生前的异常征兆,必须进行停产撤人。 第三条异常信息内容: 1.地表水无法控制,向井下溃水的; 2.井下发生突水或井下涌水量出现突增等异常情况,危及职工生命及矿井安全的; 3.井下发生瓦斯、煤尘、火灾、水灾等事故危险征兆的; 4.供电系统发生故障,不能保证矿井安全供电的; 5.主要通风机发生故障或通风系统遭到破坏,不能保证矿井正常通风的; 6.监测监控系统出现报警,情况不明的; 7.有害气体指标超限或发现有明火的; 8.井下工作地点瓦斯浓度超过规定的; 9.采掘工作面有冒顶征兆,采取措施不能有效控制的;

10.其他异常情况可能引起停产撤人的。 第三章安全生产异常信息报告、收集、分析处理制度 第四条为有效、果断处置异常信息,超前防范各类事故,成立安全生产异常信息领导小组。 组长:矿长 副组长:安全矿长、技术矿长、生产矿长、机电矿长 成员:各专业副总工程师和专业科室负责负责人 领导小组下设办公室,调度室主任兼任办公室主任,具体负责异常信息报告、收集和分析处理等有关工作。 第五条安全生产异常信息报告 任何人员一旦发现本制度规定的安全生产异常信息,必须及时向调度室报告,调度室值班人员接到安全生产异常信息的报告后,必须立即通知受到安全威胁的人员撤离危险区域,同时通知安全生产异常信息领导小组所有人员到调度室集合。 第六条安全生产异常信息收集 矿井值班、带班领导、区队跟班队长、班组长、安监员、调度员、瓦检员及其他安全管理人员,均为安全生产异常信息收集者,在矿井生产过程中,当发现本制度规定的安全生产异常信息时,必须第一时间发出停产撤人的命令,指挥现场人员撤到安全地点,同时向矿调度室汇报,调度室做好安全生产异常信息的收集记录工作。

货物异常应急处置制度

货物异常应急处置制度 一、目的 确保公司在进行货物运输、装卸、存储等过程中对货物多货、少货、货损、污染、霉变、虫害、火灾、被盗、丢失及其他异常情况进行及时调查分析和处置,并遵守国家相关安全要求。 二、范围 1、本制度适用于货物运输、装卸、存储数量及质量控制; 2、本制度使用货物异常分析及处置。 三、主要职责和权限 1、理货员负责核实出入库货物数量、质量、单据和记录的控制; 2、统计员负责提供货物单据及盘点数量; 3、业务员负责货物异常的追溯、调查及对接客户的处理方案; 4、仓库经理负责处理异常货物。 四、工作程序 1、入库货物多货、少货 (1)货物拆箱时,由理货员依据入库单,清单货物数量。如发现多货或少货,首先与统计员、操作员确认入库单数量是否正确; (2)如确认的确为装箱货物数量异常,则须拍照取证,并及时上报仓库经理、操作员; (3)操作员须及时与客户沟通确认是否继续卸货,待客户确认实际到货数量,并同意卸货后,方可安排叉车工予以卸货; (4)操作员留存客户确认实际到货数量的邮件、微信等截图。 2、出库货物多货、少货 (1)货物装箱时,由理货员依据出库单,查找对应提单号货物存储位置,并检查货物状态,是否有货损、污染、霉变、虫害等情况; (2)如存储期间出现货损、污染、霉变、虫害等情况,则及时报告仓库经理,对异常情况进行调查,必要情况下,须及时通知客户以便出具处理意见; (3)货物检查无异常的,安排并监督叉车工进行装车工作,清点装货数量。 (4)如出库货物到达客户仓库后,被告知货物多货或少货的,则由仓库经理负责调取监控,查看装车视频,清点装车数量,确定为装货时数量异常还是运输过程中数量异常; (5)如装货时少货的,除上报公司外,与客户沟通单独送货还是待下批次货物一同运输; (6)如装货时多货的,除上报公司外,与客户沟通单独退货还是待下批次货物扣除同等数量。 (7)操作员留存客户确认实际到货数量、处理意见的邮件、微信等截图。 3、入库货物货损 (1)货物拆箱时,由理货员依据入库单,检查货物包装。如发现货损情况,则须拍照取证,并及时上报仓库经理、操作员; (2)由仓库经理负责调取监控,查看车辆入场、拆箱视频,确认货损出现原因;

设备异常处理流程及规定

设备异常处理流程及规 定 文件管理序列号:[K8UY-K9IO69-O6M243-OL889-F88688]

宏飞机械厂设备异常处理流程

宏飞机械异常处理规定 1.目的 为了更好的规范和完善公司生产异常处理作业,使生产问题发生后,各部门人员迅速、有效的处理,减免停工时间,提高生产效率,特制定本流程。2.适用范围 适用于公司所有生产异常的处理。 3.职责 3.1 生产部门负责生产异常的反馈和处理措施验证。 3.2 品质部负责品质异常的处理及验证。 3.3 设备组负责设备异常的处理。 3.4 计控部负责物料异常的处理。 3.5 技术部负责技术、关键工序设备、工装模具、工艺异常的处理。 4.作业规范 4.1 生产异常反馈 4.1.1 当生产发生异常或有出现异常的趋势时,生产部发现人员和现场管理人员(如班组长)应即时给予分析,并主动积极寻求解决方法,包括与相关人员联系,如能及时解决则不在本流程规定内。 4.1.2 如情况严重,班组长不能处理或异常会导致停产时间超过30分钟时,应立即报告车间主管,由车间主管进行解决。若车间主管也不能解决时,则由

班组长根据异常现状及时开出《生产异常报告单》,经车间主管确认后,报告生产部经理. 4.1.3 生产部经理接到生产异常报告后 10 分钟内赶到现场,对问题进行分类分析,必要时与相关部门负责人联系,寻求支持或召开生产异常协调会进行解决,若相关部门不能配合时,应及时向总经理报告,由总经理协调各职能部门进行解决。 4.2 生产异常处理 4.2.1 相关部门在接到生产异常信息后 10 分钟内(紧急事件立即处理)赶到生产现场,初步分析。如部门负责人不能到现场应在规定时间内派人到场. 4.2.2 根据异常信息由生产部将《生产异常报告单》交异常处理主要责任人. 4.2.3 要求异常处理主要责任人在接到信息后 30 分钟内制定出应急措施. A 质量异常:由品管部负责主导对异常情况进行分析及处理,必要时组织相关部门专题会议讨论解决。 B 设备异常:设备异常由设备组负责对设备进行检修,如不能在规定时间内完成则需向相关生产单位说明,同时提出停产申请并回复确定修复时间,维修完成后由生产部责任班长签署维修结果。 C 物料异常:由计控部根据实际情况书面确定是否调单或代用物料生产。 D 技术、关键工序设备、工装模具、工艺异常:由技术部负责主导对异常情况进行分析及处理,必要时组织相关部门专题会议讨论解决。 4.2.4 由异常处理的主要责任部门负责30分钟内填写完成《生产异常报告单》中的应急对策栏,然后交生产部跟进。 4.2.5 异常处理主要责任部门如不能在规定时间内处理,应做出是否停产的意见,并注明预计恢复生产时间。停产必须报总经理审核后方为有效。

Linux中断处理流程

Linux中断处理流程 先从函数注册引出问题吧。 一、中断注册方法 在linux内核中用于申请中断的函数是request_irq(),函数原型在Kernel/irq/manage.c中定义: int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id) irq是要申请的硬件中断号。 handler是向系统注册的中断处理函数,是一个回调函数,中断发生时,系统调用这个函数,dev_id参数将被传递给它。 irqflags是中断处理的属性,若设置了IRQF_DISABLED (老版本中的SA_INTERRUPT,本版zhon已经不支持了),则表示中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断,慢速处理程 序不屏蔽;若设置了IRQF_SHARED (老版本中的SA_SHIRQ),则表示多个设备共享中断,若设置了IRQF_SAMPLE_RANDOM(老版本中的 SA_SAMPLE_RANDOM),表示对系统熵有贡献,对系统获取随机数有好处。(这几个flag是可以通过或的方式同时使用的) dev_id在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL。devname设置中断名称,在cat /proc/interrupts中可以看到此名称。 request_irq()返回0表示成功,返回-INVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY表示中断已经被占用且不能共享。 关于中断注册的例子,大家可在内核中搜索下request_irq。 在编写驱动的过程中,比较容易产生疑惑的地方是: 1、中断向量表在什么位置?是如何建立的? 2、从中断开始,系统是怎样执行到我自己注册的函数的? 3、中断号是如何确定的?对于硬件上有子中断的中断号如何确定? 4、中断共享是怎么回事,dev_id的作用是? 本文以2.6.26内核和S3C2410处理器为例,为大家讲解这几个问题。 二、异常向量表的建立 在ARM V4及V4T以后的大部分处理器中,中断向量表的位置可以有两个位置:一个是0,另一个是0xffff0000。可以通过CP15协处理器c1寄存器中V位(bit[13])控制。V和中断向量表的对应关系如下: V=0 ~ 0x00000000~0x0000001C V=1 ~ 0xffff0000~0xffff001C arch/arm/mm/proc-arm920.S中 .section ".text.init", #alloc, #execinstr __arm920_setup: ...... orr r0, r0, #0x2100 @ ..1. ...1 ..11 (1) //bit13=1 中断向量表基址为0xFFFF0000。R0的值将被付给CP15的C1.

1.异常处理机制(精)

1. 异常机制 异常机制是指当程序出现错误后,程序如何处理。具体来说,异常机制提供了程序退出的安全通道。当出现错误后,程序执行的流程发生改变,程序的控制权转移到异常处理器。 传统的处理异常的办法是,函数返回一个特殊的结果来表示出现异常(通常这个特殊结果是大家约定俗称的),调用该函数的程序负责检查并分析函数返回的结果。这样做有如下的弊端:例如函数返回-1代表出现异常,但是如果函数确实要返回-1这个正确的值时就会出现混淆;可读性降低,将程序代码与处理异常的代码混爹在一起;由调用函数的程序来分析错误,这就要求客户程序员对库函数有很深的了解。 异常处理的流程: ①遇到错误,方法立即结束,并不返回一个值;同时,抛出一个异常对象。 ②调用该方法的程序也不会继续执行下去,而是搜索一个可以处理该异常的异常处理器,并执行其中的代码。 2 异常的分类 异常的分类: ①异常的继承结构:基类为Throwable,Error和Exception继承Throwable,RuntimeException和IOException等继承Exception,具体的RuntimeException继承RuntimeException。 ② Error和RuntimeException及其子类成为未检查异常(unchecked),其它异常成为已检查异常(checked)。 每个类型的异常的特点 Error体系: Error类体系描述了Java运行系统中的内部错误以及资源耗尽的情形。应用程序不应该抛出这种类型的对象(一般是由虚拟机抛出)。如果出现这种错误,除了尽力使程序安全退出外,在其他方面是无能为力的。所以,在进行程序设计时,应该更关注Exception体系。 Exception体系包括RuntimeException体系和其他非RuntimeException的体系: ① RuntimeException:RuntimeException体系包括错误的类型转换、数组越界访问和试图访问空指针等等。处理RuntimeException的原则是:如果出现

Windows异常处理流程

Windows异常处理流程 作者:SoBeIt 出处:https://www.doczj.com/doc/cb468481.html,/articles/200412/761.html 日期:2005-01-06 先来说说异常和中断的区别。中断可在任何时候发生,与CPU正在执行什么指令无关,中断主要由I/O设备、处理器时钟或定时器等硬件引发,可以被允许或取消。而异常是由于CPU执行了某些指令引起的,可以包括存储器存取违规、除0或者特定调试指令等,内核也将系统服务视为异常。中断和异常更底层的区别是当广义上的中断(包括异常和硬件中断)发生时如果没有设置在服务寄存器(用命令号0xb向8259-1中断控制器0x20端口读出在服务寄存器1,用0xb向8259-2中断控制器的0xa0端口读出在服务寄存器2)相关的在服务位(每个在服务寄存器有8位,共对应IRQ 0-15)则为CPU的异常,否则为硬件中断。 下面是WINDOWS2000根据INTEL x86处理器的定义,将IDT中的前几项注册为对应的异常处理程序(不同的操作系统对此的实现标准是不一样的,这里给出的和其它一些资料不一样是因为这是windows的具体实现): 中断号名字原因 0x0 除法错误1、DIV和IDIV指令除0 2、除法结果溢出 0x1 调试陷阱1、EFLAG的TF位置位 2、执行到调试寄存器(DR0-DR4)设置的断点 3、执行INT 1指令 0x2 NMI中断将CPU的NMI输入引脚置位(该异常为硬件发生非屏蔽中断而保留) 0x3 断点执行INT 3指令 0x4 整数溢出执行INTO指令且OF位置位 0x5 BOUND边界检查错误BOUND指令比较的值在给定范围外 0x6 无效操作码指令无法识别 0x7 协处理器不可用1、CR0的EM位置位时执行任何协处理器指令 2、协处理器工作时执行了环境切换 0x8 双重异常处理异常时发生另一个异常 0x9 协处理器段超限浮点指令引用内存超过段尾 0xA 无效任务段任务段包含的描述符无效(windows不 使用TSS进行环境切换,所以发生该异常说明有其它问题) 0xB 段不存在被引用的段被换出内存 0xC 堆栈错误1、被引用内存超出堆栈段限制 2、加载入SS寄存器的描述符的present位置0 0xD 一般保护性错误所有其它异常处理例程无法处理的异常 0xE 页面错误1、访问的地址未被换入内存 2、访问操作违反页保护规则 0x10 协处理器出错CR0的EM位置位时执行W AIT或ESCape指令 0x11 对齐检查错误对齐检查开启时(EFLAG对齐位置位)访问未对齐数据

生产异常处理机制

广东樱雪有限公司文件组装车间异常工时责任追究考核管理办法(修订版)为了确保制造部月度产量目标达成,确保公司生产经营紧张有序,生产压力在各生产支持主责模块间有效传递与分解,实现不停线、不断线、不下线,及时暴露生产异常并进行有效责任追究,经公司研究决定特制定本考核管理办法。一、总装车间生产支持主责模块及必须有效支持的项目

二、主要生产异常类型与主责模块责任界定

三、各类生产异常情况责任人分解 四、生产异常情况异常工时责任追究执行标准

五、生产异常责任追究运作模式 1、组装车间在生产过程中出现异常情况时由生产线线长、物料调度(指仓管 的方式通知主责模块第一责任 人,相关责任人收到异常信息后应立即(要求在接到信息的 现场进行处理和确认,如果不到现场处理和确认则视同默认车间反馈的异常事件及处理异常对车间生产影响的时间; 2、的形式通知主责模块第一责任 人(责任人到达现场除外),同时将异常工时与责任模块第一负责人进行口头初步确认; 3、的异常情况,由生产线线长(指发泡总装线)、物料 调度(指仓管员)在填写《异常工时责任追究反馈表》在上交车间主任审核,车间主任审核完后在各生产部部长审批; 4、《异常工时责任追究反馈表》审批流程:生产线线长、仓管员(填写)→ 车间主任(审核)→各生产部部长(审批); 5、各生产部部长将审批完后的《异常工时责任追究反馈表》在 的形式发送至相关责任人处进行公示; 6、相关责任人在收到《异常工时责任追究反馈表》后默认视同接受,如果有

各生产部部长进行沟通反馈,各生产部部长收到异议反馈后组织异议调查最终将以事实依据作为最终裁定; 7、最终裁定的《异常工时责任追究反馈表》将在事件发生日的 8、生产副总助理汇总上月所有异常工时责任追究统计表输出《异 常工时责任追究月度处罚明细表》,经制生产副总(审核)、总经理(审批)后报送行政部(执行扣罚); 9、异常工时责任追究月度处罚金额在责任人当月工资中扣除; 10、各班组负责人根据本班组异常工时产生的罚款额度以 形式提交申请,经各制造部部长(一审)、生产副总(二审),生产副总(批准)后报送各生产部车间统计员将罚款额度纳入受影响班组的当月工资总额; 六、其他事项 1、本考核管理办法由生产部负责起草、修订、解释、执行; 2、本考核管理办法从2013年*月*日起试行考核; 3、为了提高各主责模块对异常工时改进的重视程度,要求各主责模块每月收 到正式版《异常工时责任追究月度处罚明细表》后的三个工作日向生产部提交《异常工时改进方案》; 4、各主责模块提交的《异常工时改进方案》,要求要对产生的异常工时进行数 据分析、原因总结、明确改进措施、落实责任人与改善进度; 5、生产部对各部门的《异常工时改进方案》进行收集、审核、评价; 6、行政部对各主责模块《异常工时改进方案》的评价结果纳入部门月度绩效 考核,根据改进的效果对责任部门实行扣分或加分;

设备异常处理流程及规定

设备异常处理流程及规定 设备异常处理流程 序流程图责任人表单作业内容号 班组长/线长不能处生产异常出现时,生产部门/设备生产异常理或异常会导致停产时间超过30分钟 1 相关部门/ 时,应立即上报,或开出《生产异常发现者报告单》进行处理。 生产部负责人接到报告后应在10分钟生产部门/内赶赴现场;必要时可同时通知相关相关人员 2 相关部门/ 部门负责人,相关部门负责人接到通赶赴现场负责人知后应在10分钟内赶到现场( 相关部门负责人到达现场后立即对异相关部门异常分析 3 常进行分析,若部门负责人不能到场负责人应在10分钟内派人到达现场( 如不能立即处理应作出是否停产的意确定是总经办/总4 见,并注明预计恢复生产的时间(停否停产经理产应由总经理批准( 相关部门负责人针对问题应在30分钟制定应急相关部门生产异常 5 内制定出应急处理措施,制定措施时处理措施负责人报告单应尽可能地降低影响生产部门生产异常生产部门按应急措施进行生产按照处理6 负责人报告单调整生产措施生产 生产部/品 质部 NG 应急措施的有效性由生产部与品质部生产异常责任人措施7 共同验证,如验证不符合则重新制定报告单验证相关措施( YES 验证结果符合生产及品质相关要求,生产部负责恢复正8 可以在恢复生产后由品质部和生产部人常生产对异常进行跟进确认(

相关责任部生产恢复正常后相关部门应对问题的生产异常 9 门深层次的原因加以分析,并在两个工制定长期报告单负责人作日内制定出长期预防措施( 预防措施 生产部生产异常生产部应协同品质部对责任部门的长10 负责人报告单期预防措施执行结果进行跟踪预防措施跟踪 异常处理规定 1(目的 为了更好的规范和完善公司生产异常处理作业,使生产问题发生后,各部门人员迅速、有效的处理,减免停工时间,提高生产效率,特制定本流程。 2(适用范围 适用于公司所有生产异常的处理。 3(职责 3(1 生产部门负责生产异常的反馈和处理措施验证。 3(2 品质部负责品质异常的处理及验证。 3(3 设备组负责设备异常的处理。 3(4 计控部负责物料异常的处理。 3(5 技术部负责技术、关键工序设备、工装模具、工艺异常的处理。 4(作业规范 4.1 生产异常反馈 4.1.1 当生产发生异常或有出现异常的趋势时,生产部发现人员和现场管理人员(如班组长)应即时给予分析,并主动积极寻求解决方法,包括与相关人员联系,如能及时解决则不在本流程规定内。 4.1.2 如情况严重,班组长不能处理或异常会导致停产时间超过30分钟时,应立即报告车间主管,由车间主管进行解决。若

设备异常处理流程及规定

宏飞机械厂设备异常处理流程

宏飞机械异常处理规定 1.目的 为了更好的规范和完善公司生产异常处理作业,使生产问题发生后,各部门人员迅速、有效的处理,减免停工时间,提高生产效率,特制定本流程。 2.适用范围 适用于公司所有生产异常的处理。 3.职责 3.1 生产部门负责生产异常的反馈和处理措施验证。 3.2 品质部负责品质异常的处理及验证。 3.3 设备组负责设备异常的处理。 3.4 计控部负责物料异常的处理。 3.5 技术部负责技术、关键工序设备、工装模具、工艺异常的处理。4.作业规范 4.1 生产异常反馈 4.1.1 当生产发生异常或有出现异常的趋势时,生产部发现人员和现场管理人员(如班组长)应即时给予分析,并主动积极寻求解决方法,包括与相关人员联系,如能及时解决则不在本流程规定内。 4.1.2 如情况严重,班组长不能处理或异常会导致停产时间超过30分钟时,应立即报告车间主管,由车间主管进行解决。若车间主管也不能解决时,则由班组长根据异常现状及时开出《生产异常报告单》,经车间主管确认后,报告生产部经理.

4.1.3 生产部经理接到生产异常报告后 10 分钟内赶到现场,对问题进行分类分析,必要时与相关部门负责人联系,寻求支持或召开生产异常协调会进行解决,若相关部门不能配合时,应及时向总经理报告,由总经理协调各职能部门进行解决。 4.2 生产异常处理 4.2.1 相关部门在接到生产异常信息后 10 分钟内(紧急事件立即处理)赶到生产现场,初步分析。如部门负责人不能到现场应在规定时间内派人到场. 4.2.2 根据异常信息由生产部将《生产异常报告单》交异常处理主要责任人. 4.2.3 要求异常处理主要责任人在接到信息后 30 分钟内制定出应急措施. A 质量异常:由品管部负责主导对异常情况进行分析及处理,必要时组织相关部门专题会议讨论解决。 B 设备异常:设备异常由设备组负责对设备进行检修,如不能在规定时间内完成则需向相关生产单位说明,同时提出停产申请并回复确定修复时间,维修完成后由生产部责任班长签署维修结果。 C 物料异常:由计控部根据实际情况书面确定是否调单或代用物料生产。 D 技术、关键工序设备、工装模具、工艺异常:由技术部负责主导对异常情况进行分析及处理,必要时组织相关部门专题会议讨论解决。 4.2.4 由异常处理的主要责任部门负责30分钟内填写完成《生产异

详解异常处理机制

详解异常处理机制 当一个程序出现错误时,它可能的情况有3种:语法错误,运行时错误和逻辑错误。语法错误是指代码的格式错了,或者某个字母输错了;运行时错误是指在程序运行的时候出现的一些么有想到的错误,如:空指针异常,数组越界,除数为零等;逻辑错误是指运行结果与预想的结果不一样,这是一种很难调试的错误。 AD: 当一个程序出现错误时,它可能的情况有3种:语法错误,运行时错误和逻辑错误。语法错误是指代码的格式错了,或者某个字母输错了;运行时错误是指在程序运行的时候出现的一些么有想到的错误,如:空指针异常,数组越界,除数为零等;逻辑错误是指运行结果与预想的结果不一样,这是一种很难调试的错误。而java中的异常处理机制主要是指处理运行时错误,即异常就是运行时错误。 产生异常的原因有3中:1.java内部发生错误,java虚拟机产生的异常。2.编写程序的时候由于错误引起的异常,如:空指针异常,数组越界等。3.通过throw语句生成的异常。这种异常通常称为“检查异常”,用来告知方法的调用着相关信息。 java通过面向对象的方法处理异常。在一个方法的运行过程中如果出现了异常,这个方法就会产生代表该异常的一个对象,把它交给运行时系统,运行时系统寻找相应的代码来处理这一异常。其中,生成异常对象,并把它交个运行时系统的过程称为抛出(throw)。运行时系统在方法的调用栈中查找,直到找到能处理该异常的对象的过程称为捕获(catch)。 一、异常的分类 在java中,任何异常都属于https://www.doczj.com/doc/cb468481.html,ng.Throwable类或其子类,Throwable类又分为Exception类和Error 类。其中Exception类用于用户所出现的各种异常,它也可用来创建自定义类型的类。Error类异常是指java程序运行时与运行是系统本身有关的错误,这些异常通常是灾难性的,不是程序可以控制的。 Exception类的异常又分为运行时异常和非运行时异常。其中运行时异常是指RuntimeException类及其子类,这些异常不检查异常,可以选择捕获处理,也不可以不处理。非运行时错误是除RuntimeException类以外的异常,这些异常必须处理,否则会导致程序编译不能通过。 二、异常处理机制 处理异常的方式主要有两种:积极的处理方式和消极的处理方式。 积极的处理方式主要是使用try...catch,程序先执行try中的语句,当try中的语句出现异常时,就停止当前程序的执行,转入到catch中执行语句,若catch语句中出现异常,程序也会停止执行,然后转到catch的下一个程序继续执行。也就是说try语句用来发现异常,而catch语句用来处理异常。通常在catch 语句后会跟一个final语句,与try...catch语句结合使用,无论try和catch中是否出现异常,final 中的语句都会直线。如果一个方法必须返回一个值的话,那么把return语句放到final中最合适了,因为要是放到try或catch中的任何一个,如果运行过程中出现异常的话,程序就会停止执行,也就无法执行retrun了。

相关主题
文本预览
相关文档 最新文档