[笔记]Windows核心编程《二十三》结构化异常处理

系列文章目录

[笔记]Windows核心编程《一》错误处理、字符编码
[笔记]Windows核心编程《二》内核对象
[笔记]Windows核心编程《三》进程
[笔记]Windows核心编程《四》作业
[笔记]快乐的LInux命令行《五》什么是shell
[笔记]Windows核心编程《五》线程基础
[笔记]Windows核心编程《六》线程调度、优先级和关联性
[笔记]Windows核心编程《七》用户模式下的线程同步
[笔记]Windows核心编程《八》用内核对象进行线程同步
[笔记]Windows核心编程《九》同步设备I/O和异步设备I/O
[笔记]Windows核心编程《十一》Windows线程池
[笔记]Windows核心编程《十二》纤程
[笔记]Windows核心编程《十三》windows内存体系结构
[笔记]Windows核心编程《十四》探索虚拟内存
[笔记]Windows核心编程《十五》在应用程序中使用虚拟内存
[笔记]Windows核心编程《十六》线程栈
[笔记]Windows核心编程《十七》内存映射文件
[笔记]Windows核心编程《十八》堆栈
[笔记]Windows核心编程《十九》DLL基础
[笔记]Windows核心编程《二十》DLL的高级操作技术
[笔记]Windows核心编程《二十一》线程本地存储器TLS
[笔记]Windows核心编程《二十二》注入DLL和拦截API
[笔记]Windows核心编程《二十三》结构化异常处理

前言

结构化异常处理,SEH(Structured Exception Handling)

SEH的好处:

  • 就是当你编写程序时,只需要关注程序要完成的任务。如果在运行时发生什么错误,系统会发现并将发生的问题通知你。
  • 使用 S E H所造成的负担主要由编译程序来承担,而不是由操作系统承担。当异常块(exception block)出现时,编译程序要生成特殊的代码。编译程序必须产生一些表( t a b l e)来支持处理S E H的数据结构。编译程序还必须提供回调( c a l l b a c k)函数,操作系统可以调用这些函数,保证异常块被处理。编译程序还要负责准备栈结构和其他内部信息,供操作系统使用
    和参考。

SEH 包含两个主要功能:

  • 结束处理
  • 异常处理

SEH和c++异常处理区别:
不要将结构化异常处理同 C + +的异常处理相混淆。C + +异常处理是一种不同形式的异常处理,其形式是使用C + +关键字catch和throw。微软的Visual C++也支持C + +的异常处理,并且在内部实现上利用了已经引入到编译程序和 Windows操作系统的结构化异常处理的功能。

一个结束处理程序能够确保去调用和执行一个代码块(结束处理程序,termination handler),
而不管另外一段代码(保护体, guarded body)是如何退出的。结束处理程序的文法结构(使用微软的Visual C++编译程序)如下:

__try
{ //Guard body
...
}
__finally
{ // Terminal Handler
...
}

__try和__finally关键字用来标出结束处理程序两段代码的轮廓。在上面的代码段中,操作系统和编译程序共同来确保结束处理程序中的__finally 代码块能够被执行,不管保护体(try块)是如何退出的。不论你在保护体中使用 return,还是goto,或者是longjump,结束处理程序(finally块)都将被调用。

总之,c++异常处理顺序是:
1.try 2.finally 3. try里的return或者finally之后的代码

C++异常处理的情况

情况1 释放信标情况

代码要等待信标( semaphore),改变保护数据的内容,保存局部变量dwTemp的新值,释放信标,将新值返回给调用程序。

DwORD Funcenstein1( ) {
	DWORD dwTemp;
	//1. Do any processing here.
	__try {
	//2. Request permission to access
	//protected data. and then use it.waitForSing1e0bject(g_hSem,INFINITE);g_dwProtectedData = 5;
	dwTemp = g_dwProtectedData ;
	}
	__fina1ly {
	 //3.A1low others to use protected data.Re1easeSemaphore( g_hSem,1,NULL);
	}
	//4. Continue processing.
	return(dwTemp);
}

最后的释放信标放在finally 是一定会执行的。

情况2 释放信标 try中加入return

DwORD dwTemp;
		// 1. Do any processing here.:

	__try {
		// 2. Request permission to access
		// protected data, and then 'use it.
		waitForSing1e0bject(g_hSem,INFINITE);
		g_dwProtectedData = 5;
		dwTemp = g_dwProtectedData ;
		//Return the new value.
		return(dwTemp) ;
	}
	__fina1ly {
		//3.Al1ow others to use protected 
		data.Re1easeSemaphore( g_hSem,1NULL);
	}
	// Continue processing--this code
	// wi11 never execute in this version.dwTemp = 9;
	return(dwTemp );
}

结果是,返回5。因为当提前return时,finally会先执行,然后再执行try内的return。

当return语句试图退出try块时,编译程序要确保finally块中的代码首先被执行。要保证finally块中的代码在try块中的return语句退出之前执行。Funcens tein2中,将对ReleaseSemaphore的调用放在结束处理程序块中,保证信标总会被释放。这样就不会造成一个线程一直占有信标,否则将意味着所有其他等待信标的线程永远不会被分配CP U时间。

编译程序是如何保证在try块可以退出之前执行finally块的?
查看总结4

情况3 释放信标 在try内使用Goto

DwORD Funcenstein3( ) {
	DwORD dwTemp;
	//1. Do any processing here.
	__try {
		//2. Request permission to access
		//protected data, and then use it.
		WaitForSingle0bject( g_hSem,INFINITE);g_dwProtectedData = 5;
		dwTemp = gdwProtectedData;
		//Try to jump over the fina11y b1ock.
		goto ReturnValue;
	}
	__fina11y {
		//3.Al1ow others to use protected data.
		Re1easeSemaphore( g_hSem,1.NULL);
	}
	dwTemp = 9;
	//4. Continue processing.Returnvalue:
	return(dwTemp);
}

结果是:goto不会执行。会先try到goto时先finally在到retunrn(dwTemp)。

当编译程序看到try块中的goto语句,它首先生成一个局部展开来执行
finally 块中的内容。这一次,在 finally 块中的代码执行之后,在 ReturnValue 标号之后的代码将执行,因为在try块和finally块中都没有返回发生。这里的代码使函数返回 5。而且,由于中断了从try块到finally块的自然流程,可能要蒙受很大的性能损失(取决于运行程序的CPU)。

情况4 释放信标 try调用函数抛异常

try块中的Funcinator函数调用包含一个错误,会引起一个无效内存访问。

DMORD Funcfurter1(){
	DWORD dwTemp;
    //1. Do any processing here.
	__try{
		//2. Request permission to access
		//protected data. and then use it.
		WaitForSing1e0bject( g_hSem,INFINITE);
		dwTemp = Funcinator( g_dwProtectedData) ;
	}
	__final1y {
		//3.A11ow others to use protected data.
		ReleaseSemaphore( g .hSem,1NULL);
	)
	//4. Continue processing.
	return( dwTemp);
)

结果是,正常释放信标,进程不会提前结束,导致信标没释放。不然就可能引起其他进程等待。

如果结束处理程序足够强,能够捕捉由于无效内存访问而结束的进程,我们就可以相信它也能够捕捉setjump和longjump的结合,还有那些简单语句如break和continue。

情况5 循环中使用try-finally

如下代码执行结果如何?

DWORD FuncaDoodleDoo( ) {
    DWORD dwTemp = 0;
	
    while (dwTemp < 10){
		__try {
			if ( dwTemp == 2)
				continue;
			if ( dwTemp == 3 )
				break;
		}
   	   __fina11y {
		 	dwTemp+t;
       }
	   dwTemp++;
  }
  dwTemp += 10;
  return(dwTemp);
}

总结

1.SEH(Structured Exception Handling)和SJLJ(SetJump LongJump)区别对比

SEH:

2.SEH和c++异常处理区别?

SEH和c++异常处理区别:
不要将结构化异常处理同 C + +的异常处理相混淆。C + +异常处理是一种不同形式的异常处理,其形式是使用C + +关键字catch和throw。微软的Visual C++也支持C + +的异常处理,并且在内部实现上利用了已经引入到编译程序和 Windows操作系统的结构化异常处理的功能。

3.C++异常处理的特点?

  1. 通过使用结束处理程序,可以避免 return语句的过早执行。
    例如:try内return时 finally可以避免提前返回而没有释放。

注意除了以下几种情况不能处理:

  • 当调用ExitThread或ExitProcess时,将立即结束线程或进程,而不会执行 finally块中的任何代码。
  • 另外,如果由于某个程序调用 TerminalThread或 TerminalProcess,线程或进程将死掉,finally 块中的代码也不执行。
  • 某些 C运行期函数(例如abort)要调用ExitProcess,也使finally块中的代码不能执行。

4. 编译程序是如何保证在try块可以退出之前执行finally块的?

当编译程序检查源代码时,它看到在try块中有return语句。这样,编译程序就生成代码将返回值 保存在一个编译程序建立的临时变量中。编译程序然后再生成代码来执行finally块中包含的指令,这称为局部展开。更特殊的情况是,由于try块中存在过早退出的代码,从而产生局部展开,导致系统执行finall y块中的内容。在finally块中的指令执行之后,编译程序临时变量的值被取出并从函数中返回。

故在编写代码时,就应该避免引起结束处理程序的t r y块中的过早退出,因为程序的性能会受到影响。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二进制怪兽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值