博文

第 30 集 Java 异常处理模型之细节分析(2006-01-24 13:40:00)

摘要: 上一篇文章中,与其是说对 Java 语言中的异常处理模型展开讨论;倒不如是说,把 Java 异常处理模型与 C++ 异常处理模型展开了全面的比较和分析。俗话说得好,“不怕不识货,就怕货比货”,因此,通过与 C++ 异常处理模型的综合比较,使我们能够更清楚地认识 Java 中异常处理模型的特点、优势,以及设计时的一些背景及原因。 这篇文章,阿愚打算对 Java 异常处理模型中的一些重要的细节问题展开详细讨论,让我们更上一层楼吧! finally 区域内的代码块在 return 之前被执行 由于 Java 程序中,所有的对象都是在堆上( Heap )分配存储空间的,这些空间完全由垃圾回收机制来对它们进行管理。因此,从这一点可以分析得出一个推论: Java 中的异常处理模型的实现,其实要比 C++ 异常处理模型简单得多。例如,它首先不需要像 C++ 异常处理模型中那样,必须要跟踪栈上的每一个“对象”的构造和析构过程(只有跟踪并掌握了这些信息,发生异常时, C++ 系统它才会知道当前应该析构销毁哪些对象呀!),这是因为 Java 程序中,栈上是绝对没有“对象”的(实际只是对堆上对象的引用)。另外,还有 Java 语言中的异常对象的传递也更为简单和容易了,它只需传递一个引用指针而已,而完全不用考虑异常对象的构造、复制和销毁过程。 当然, Java 异常处理模型较 C++ 异常处理模型复杂的地方是,它引入了 finally 机制(主要用于数据库连接的关闭、 Socket 关闭、文件流的关闭等)。其实,我们也知道 finally 语法最早是在微软的 SEH 所设计出的一种机制,虽然它功能很强大,但是实现起来却并不是很难,从表象上来理解:当代码在执行过程中,遭遇到 return 和 goto 等类似的语句所引发作用域(代码执行流)转移时,便会产生一个局部展开( Local Unwinding );而由于异常而导致的 finally 块被执行的过程,往往被称为全局展开( Global Unwinding )。由于展开( Unwinding )而导致的 finally 块被执行的过程,非常类似于一个子函数(或子过程)被调用的过程。例如,当在 try 块中最后一条语句 return 被执行到的时候,一个展开操作便发生了,可以把展开操作想象成,是编译器在 return 语......

阅读全文(2896) | 评论:0

第29集 Java语言中的异常处理模型(2006-01-24 13:40:00)

摘要: 对于一个非常熟悉 C++ 异常处理模型的程序员来说,它几乎可以不经任何其它培训和学习,就可以完全接受和能够轻松地使用 Java 语言中的异常处理编程方法。这是因为 Java 语言中的异常处理模型几乎与 C++ 中异常处理模型有 99% 的相似度,无论是从语法规则,还是语义上来说,它们二者都几乎完全一致。 当然,如果你对 Java 语言中的异常处理模型有更多,或更深入的了解,你还是能够发现 Java 异常处理模型与 C++ 中异常处理模型还是存在不少差别的。是的, Java 语言本来就是 C++ 语言的完善改进版,所以, Java 语言中的异常处理模型也就必然会继承了 C++ 异常处理模型的风格和优点。但是,好的东西不仅仅是需要继承优点,更重要的是需要“去其糟粕,取其精华”,需要发展!!!毫无疑问, Java 语言中的异常处理模型完全达到了这一“发展”高度。它比 C++ 异常处理模型更安全,更可高,更强大和更丰富。 下面的内容中,阿愚不打算再向大家详细介绍一遍有关 Java 异常处理编程的具体语法和规则。因为这与 C++ 中的异常处理几乎完全一样,而且这些基础知识在太多太多的有关 java 编程的书籍中都有详细阐述。而阿愚认为: 这里更需要的是总结,需要的是比较,需要的是重点突出,需要的是关键之处 。所以,下面着重把 Java 语言中的异常处理模型与 C++ 异常处理模型展开比较,让我们透彻分析它到底有何发展?有何优势?与 C++ 异常处理模型到底有哪些细节上的不同?又为什么要这样做? 借鉴并引进了 SEH 异常模型中的 try-finally 语法 要说 Java 异常处理模型与 C++ 中异常处理模型的最大不同之处,那就是在 Java 异常处理模型中引入了 try-finally 语法,阿愚认为这是从微软的 SEH 借鉴而来。在前面的一些文章中,详细而深入阐述 SEH 异常处理模型的时候,我们从中获知, SEH 主要是为 C 语言而设计的,便于第三厂商开发的 Window 驱动程序有更好更高的安全保障。同时, SEH 异常处理模型中除了 try-except 来用于处理异常外,还有一个 try-finally 语法,它主要用来清除一些曾经分配的资源(由于异常出现,而导致这些资源不能够按正常的顺序被释放,还记得吗?这被称为“ UNWIND ”), ......

阅读全文(3060) | 评论:0

第28集 如何把SEH类型的系统异常转化为C++类型的异常?(2006-01-24 13:39:00)

摘要: 在上一篇文章中,详细讨论了“ SEH 与 C++ 异常模型的混合使用”,这一篇文章中,主人公阿愚仍将这一主题继续深入,那就是“如何把 SEH 类型的系统异常转化为 C++ 类型的异常?”(其实,这本质上仍然属于 SEH 与 C++ 异常模型的混合使用,也即 C++ 异常模型来捕获 SEH 系统类型的异常)。 为什么要 把 SEH 类型的系统异常转化为 C++ 类型的异常? 做一件事情之前,我们最好要搞清为什么!“十万个为什么” 可曾造就了多少顶级奇才!呵呵! WHY? ? WHY ? WHY ?这对任何一个人来说,都绝对是个好习惯,阿愚同学就一直把这个当“宝贝”。那么,究竟 为什么要 把 SEH 类型的系统异常转化为 C++ 类型的异常?朋友们,大家都想想,整理整理自己的意见和想法。这里,阿愚给出它个人的理解,如下: •  首先是由于我们在编程时,仍然最好遵循 MSDN 给出的建议和忠告(“ C 程序中使用 try-except 和 try-finally ;而 C++ 程序则应该使用 try-catch ”)。但是,为了提高程序的可靠性(防止意外系统异常的出现,而导致的程序无规则崩溃现象),我们还必须要求在 C++ 程序中,使用 try-catch 模型 能够捕获并 处理系统异常, 这在第 3 集的文章中曾详细讨论过了它, 那就是它只能采用 catch(…) 语法来捕获 SEH 类型的系统异常。 catch(…) 的使用 虽然一定程度上提高了程序的可靠性,但是,“异常发生时的许多相关信息”它却什么也没有提供给程序员(包括何种类型的系统异常,出现的地点,以及其它有关异常的信息等等)。因此,我们需要一种有效途径,来把 SEH 类型的系统异常转化为 C++ 类型的异常?这无疑也就提供了一种在 C++ 异常处理模型中,不仅能够处理系统异常,而且还能够获取有关 SEH 类型系统异常中的许多详细信息的手段。 •  其次就是,阿愚多次阐述过, C++ 异常处理是和面向对象紧密联系的(它二位可是“哥俩好”),因此,如果把 SEH 类型的系统异常统一到面向对象体系结构设计的“异常分类”中去,那对程序员而言,岂不是妙哉!美哉!该解决方案真所谓是,即提高了可靠性;又不失优雅! 如何实现 把 SEH 类型的系统异常转化为 ......

阅读全文(4028) | 评论:1

第 27 集 SEH 与 C++ 异常模型的混合使用(2006-01-24 13:38:00)

摘要: 在上一篇文章中我们看到了,在 C++ 程序中可以能够很好地使用 SEH 的 try-except 和 try-finally 机制(虽然 MSDN 中不建议这样做),这一篇文章中我们继续讨论,在 C++ 程序中同时使用 try-except 异常机制( SEH )和 try-catch 异常机制( C++ 异常模型 )的情况。 朋友们,准备好了心情吗?这可是有点复杂呦! 如何混合使用呢? 同样,还是看例子先。仍然是在原来例程的代码基础上做修改,修改后的代码如下: // 注意,这是 C++ 程序,文件名为: SEH-test.cpp #include "stdio.h" class A { public: void f1() {} // 抛出 C++ 异常 void f2() { throw 888;} }; // 这个函数中使用了 try-catch 处理异常,也即 C++ 异常处理 void test1() { A a1; A a2,a3; try { a2.f1(); a3.f2(); } catch(int errorcode) { printf("catch exception,error code:%d\n", errorcode); } } // 这个函数没什么改变,仍然采用 try-except 异常机制,也即 SEH 机制 void test() { int* p = 0x00000000; // pointer to NULL __try { // 这里调用 test1 函数 test1(); puts("in try"); __try { puts("in try"); // causes an access violation exception; // 导致一个存储异常 *p = 13; puts(" 这里不会被执行到 "); } __finally { puts("in finally"); } puts(" 这里也不会被执行到 "); } __except(puts("in filter 1"), 0) { puts("in e......

阅读全文(5333) | 评论:0

第 26 集 SEH 可以在 C++ 程序中使用(2006-01-24 13:37:00)

摘要:  首先声明的是, C++ 中的异常处理机制是建立在 Windows 平台上的 SEH 机制之上,所以 SEH 当然可以在 C++ 程序中使用。不过“阿愚”多次强调过,我们平常一般狭义上的 SEH 都是指 try-except 和 try-finally 异常机制,而它们是给 C 语言( VC 环境)编写 windows driver 而设计的,所以 SEH 主要应该在 C 程序中被使用,而 C++ 程序则应该使用 try-catch 机制的 C++ 异常处理模型( micorsoft 的 MSDN 一直强烈建议程序员遵循此规则)。但是, SEH 到底能在 C++ 程序中使用吗?“当然可以,肯定可以”,其实在一开始阐述 Windows 平台多种异常机制之间的关系时,就已经清楚地表明了这一点。 这篇文章系统地来看看 SEH 在 C++ 程序中的各种使用情况。 先来一个简单的例子 其实简单的例子,就是把以前的使用 SEH 机制的 C 程序,改称 C++ 程序,看它能正常编译和运行否?什么意思,很简单,就是把原来的 .c 程序的扩展名改为 .cpp 文件,也即此时 VC 编译器会采用 C++ 语言来编译此程序(这也即为 C++ 程序了)。朋友们试试吧!代码如下: // 注意,这是 C++ 程序,文件名为: SEH-test.cpp #include "stdio.h" void test() { int* p = 0x00000000; // pointer to NULL __try { puts("in try"); __try { puts("in try"); // causes an access violation exception; // 导致一个存储异常 *p = 13; puts(" 这里不会被执行到 "); } __finally { puts("in finally"); } puts(" 这里也不会被执行到 "); } __except(puts("in filter 1"), 0) { puts("in except 1"); } } void main() { puts("hello"); __try {......

阅读全文(3682) | 评论:0

第25集 SEH的综合(2006-01-23 13:22:00)

摘要:   SEH模型主要包括try-except异常处理机制和try-finally结束处理机制,而且这两者能够很好地有机统一起来,它们结合使用时,能够提供给程序员非常强大、非常灵活的控制手段。其实这在上一篇文章中的几个例子中已经使用到,这里将继续进行系统的介绍,特别是try-except和try-finally结合使用时的一些细节问题。

try-except和try-finally组合使用

  try-except和try-finally可以组合起来使用,它们可以是平行线性的关系,也可以是嵌套的关系。而且不仅是try-except语句中可以嵌套try-finally语句;同时try-finally语句中也可以嵌套try-except语句。所以它们的使用起来非常灵活,请看下面的代码: // 例程1,try-except语句与try-finally语句平行关系
#include <stdio.h>
void main()
{
puts("hello"); __try
{
int* p;
puts("__try块中"); // 下面抛出一个异常
p = 0;
*p = 25;
}
__except(1)
{
puts("__except块中");
}

__try
{
}
__finally
{
puts("__finally块中");
} puts("world");
}
// 例程2,try-except语句中嵌套try-finally
#include <stdio.h> void main()
{
puts("hello");

__try
{
__try
{
int* p;
puts("__try块中"); // 下面抛出一个异常
p = 0;
*p = 25;
}
__finally
{
// 这里会被执行吗
puts("__finally块中");
}
}
__except(1)
{
puts("__except块中")......

阅读全文(3875) | 评论:1

第24集 SEH的强大功能之二(2006-01-23 13:21:00)

摘要:  上一篇文章讲述了SEH的异常处理机制,也即try-except模型的使用规则。本篇文章继续探讨SEH另外一项很重要的机制,那就是“有效保证资源的清除”,其实这才是SEH设计上最为精华的一个东东,对于C程序而言,它贡献简直是太大了。

  SEH的这项机制被称为结束处理(Termination Handling),它是通过try-finally语句来实现的,下面开始讨论吧!

try-finally的作用

  对于try-finally的作用,还是先看看MSDN中怎么说的吧!摘略如下: The try-finally statement is a Microsoft extension to the C and C++ languages that enables 32-bit target applications to guarantee execution of cleanup code when execution of a block of code is interrupted. Cleanup consists of such tasks as deallocating memory, closing files, and releasing file handles. The try-finally statement is especially useful for routines that have several places where a check is made for an error that could cause premature return from the routine.   上面的这段话的内容翻译如下:   try-finally语句是Microsoft对C和C++语言的扩展,它能使32位的目标程序在异常出现时,有效保证一些资源能够被及时清除,这些资源的清除任务可以包括例如内存的释放,文件的关闭,文件句柄的释放等等。try-finally语句特别适合这样的情况下使用,例如一个例程(函数)中,有几个地方需要检测一个错误,并且在错误出现时,函数可能提前返回。 try-finally的语法规则

   上面描述try-finally机制的有关作用时,也许一时......

阅读全文(3879) | 评论:0

第23集 SEH的强大功能之一(2006-01-23 13:20:00)

摘要:  从本篇文章开始,将全面阐述__try,__except,__finally,__leave异常模型机制,它也即是Windows系列操作系统平台上提供的SEH模型。主人公阿愚将在这里与大家分享SEH的学习过程和经验总结。

  SEH有两项非常强大的功能。当然,首先是异常处理模型了,因此,这篇文章首先深入阐述SEH提供的异常处理模型。另外,SEH还有一个特别强大的功能,这将在下一篇文章中进行详细介绍。

try-except入门

  SEH的异常处理模型主要由try-except语句来完成,它与标准C++所定义的异常处理模型非常类似,也都是可以定义出受监控的代码模块,以及定义异常处理模块等。还是老办法,看一个例子先,代码如下: //seh-test.c
#include <stdio.h> void main()
{
puts("hello");
// 定义受监控的代码模块
__try
{
puts("in try");
}
//定义异常处理模块
__except(1)
{
puts("in except");
}
puts("world");
}   呵呵!是不是很简单,而且与C++异常处理模型很相似。当然,为了与C++异常处理模型相区别,VC编译器对关键字做了少许变动。首先是在每个关键字加上两个下划线作为前缀,这样既保持了语义上的一致性,另外也尽最大可能来避免了关键字的有可能造成名字冲突而引起的麻烦等;其次,C++异常处理模型是使用catch关键字来定义异常处理模块,而SEH是采用__except关键字来定义。并且,catch关键字后面往往好像接受一个函数参数一样,可以是各种类型的异常数据对象;但是__except关键字则不同,它后面跟的却是一个表达式(可以是各种类型的表达式,后面会进一步分析)。

try-except进阶

  与C++异常处理模型很相似,在一个函数中,可以有多个try-except语句。它们可以是一个平面的线性结构,也可以是分层的嵌套结构。例程代码如下: // 例程1
// 平面的线性结构
#include <stdio.h> void ma......

阅读全文(2715) | 评论:0

第22集 更进一步认识SEH (2006-01-23 13:20:00)

摘要:   上一篇文章阿愚对结构化异常处理(Structured Exception Handling,SEH)有了初步的认识,而且也知道了SEH是__try,__except,__finally,__leave异常模型机制和try,catch,throw方式的C++异常模型的奠基石。

  为了更进一步认识SEH机制,更深刻的理解SEH与__try,__except,__finally,__leave异常模型机制的区别。本篇文章特别对狭义上的SEH进行一些极为细致的讲解。

SEH设计思路

  SEH机制大致被设计成这样一种工作流程:用户应用程序对一些可能出现异常错误的代码模块,创建一个相对应的监控函数(也即回调函数),并向操作系统注册;这样应用程序在执行过程中,如果什么异常也没出现的话,那么程序将按正常的执行流顺序来完成相对应的工作任务;否则,如果受监控的代码模块中,在运行时出现一个预知或未预知的异常错误,那么操作系统将捕获住这个异常,并暂停程序的执行过程(实际上是出现异常的工作线程被暂停了),然后,操作系统也收集一些有关异常的信息(例如,异常出现的地点,线程的工作环境,异常的错误种类,以及其它一些特殊的字段等),接着,操作系统根据先前应用程序注册的监控性质的回调函数,来查询当前模块所对应的监控函数,找到之后,便立即来回调它,并且传递一些必要的异常的信息作为监控函数的参数。此时,用户注册的监控函数便可以根据异常错误的种类和严重程度来进行分别处理,例如终止程序,或者试图恢复错误后,再使程序正常运行。

  细心的朋友们现在可能想到,用户应用程序如何来向操作系统注册一系列的监控函数呢?其实SEH设计的巧妙之处就在与此,它这里有两个关键之处。其一,就是每个线程为一个完全独立的注册主体,线程间互不干扰,也即每个线程所注册的所有监控回调函数会连成一个链表,并且链表头被保存在与线程本地存储数据相关的区域(也即FS数据段区域,这是Windows操作系统的设计范畴,FS段中的数据一般都是一些线程相关的本地数据信息,例如FS:[0]就是保存监控回调函数数据结构体的链表头。有关线程相关的本地数据,这里不再详细赘述,感兴趣的朋友可以参考其它更为详细地资料);其二,那就是每个存储监控回调函数指针的数据结构体,实际上它们一般并不是被存储在堆(H......

阅读全文(4128) | 评论:1

第21集 Windows系列操作系统平台中所提供的异常处理机制(2006-01-23 13:19:00)

摘要:  大家现在知道,在C++中有完善的异常处理机制,同样在C语言中也有很不错的异常处理机制来支持,另外在其它许多现代编程语言中,也都有各自的异常处理编程机制,如Ada语言等。那么为什么现在此处还在讨论,操作系统平台中所提供的异常处理机制呢?这是因为在许多系统中,编程语言所提供的异常处理机制的实现,都是建立在操作系统中所提供的异常处理机制之上,如Windows平台上的VC编译器所实现的C++异常处理模型,它就是建立在SEH机制之上的,在“爱的秘密”篇中,探讨VC中异常处理模型实现的时候,会进行更深入的研究和分析。另外,当然也有其它的系统,如Linux操作系统上的gcc就没有采用到操作系统中所提供的异常处理机制。但是这样会有一个很大的缺点,那就是对于应用程序的开发者而言,它不能够很好在自己的应用程序中,来有效控制操作系统中所出现的一些意外的系统异常,例如程序执行过程中可能出现的段错误,被0除等计算异常,以及其它许多不同类型的系统异常等。所以Linux操作系统上的gcc编译的程序中,它只能捕获程序中,曾经被自己显式地throw出来的异常,而对于系统异常,catch block是毫无办法的。

  因此,操作系统平台中所提供的异常处理机制是非常有必要的。而且,异常处理机制的实现也是操作系统设计时的一个重要课题。通常,类Unix操作系统所提供的异常处理机制是大家非常熟悉的,那就是操作系统中的信号量处理(Signal Handling),好像这也应该是Posix标准所定义异常处理接口,因此Window系列操作系统平台也支持这种机制,“信号量处理”编程机制也会在后面的章节中进一步深入讨论。而现在(以及在接下来的几篇文章中),将全面阐述Window系列操作系统平台提供的另外一种更完善的异常处理机制,那就是大名鼎鼎的结构化异常处理(Structured Exception Handling,SEH)的编程方法。

SEH设计的主要动机

  下面是出自《Window核心编程》中一小段内容的引用:   “微软在Windows中引入SEH的主要动机是为了便于操作系统本身的开发。操作系统的开发人员使用SEH,使得系统更加强壮。我们也可以使用SEH,使我们的自己的程序更加强壮。使用SEH所造成的负担主要由编译程序来承担,而不是由操作系统承担。当异常块(e......

阅读全文(3749) | 评论:0