怎么样不捕获异常而是立刻中断程序?

2021-03-30 20:02发布

8条回答
香蕉牛油果酸奶
2楼 · 2021-03-31 10:04

测试过程:
    操作系统更新到最新的windows10版本,在C#下写一个最简单的抛出异常的程序,成生Debug的64位可执行文件。
    在Windows10下双击运行让它报异常,结果程序没有任何反应,等两秒自动关闭了,没有任何提示。

注:在VisualStudio环境下是能捕获并提示异常的,但现在在Windows下直接运行遇到异常并不捕获和提示,而是直接关闭程序。

RuntimeException可以不catch,出错会自动向上抛出,直到最后都没有catch就jvm异常退出
所以异常应该继承自RuntimeException,否则在方法定义的时候抛出了异常,在调用方法时就必须catch,除非调用方法的方法也是定义为抛出异常

清屿
4楼 · 2021-04-04 10:33

RuntimeException可以不catch,出错会自动向上抛出,直到最后都没有catch就jvm异常退出
所以异常应该继承自RuntimeException,否则在方法定义的时候抛出了异常,在调用方法时就必须catch,除非调用方法的方法也是定义为抛出异常

我是大脸猫
5楼 · 2021-04-05 20:58

中断

首先讲下硬中断和软中断的区别


硬中断:硬中断是由硬件产生的,比如,像磁盘,网卡,键盘,时钟等。每个设备或设备集都有它自己的IRQ(中断请求)。基于IRQ,CPU可以将相应的请求分发到对应的硬件驱动上(注:硬件驱动通常是内核中的一个子程序,而不是一个独立的进程)。


软中断:

1. 软中断的处理非常像硬中断。然而,它们仅仅是由当前正在运行的进程所产生的。

2. 通常,软中断是一些对I/O的请求。这些请求会调用内核中可以调度I/O发生的程序。对于某些设备,I/O请求需要被立即处理,而磁盘I/O请求通常可以排队并且可以稍后处理。根据I/O模型的不同,进程或许会被挂起直到I/O完成,此时内核调度器就会选择另一个进程去运行。I/O可以在进程之间产生并且调度过程通常和磁盘I/O的方式是相同。

3. 软中断仅与内核相联系。而内核主要负责对需要运行的任何其他的进程进行调度。一些内核允许设备驱动的一些部分存在于用户空间,并且当需要的时候内核也会调度这个进程去运行。

4. 软中断并不会直接中断CPU。也只有当前正在运行的代码(或进程)才会产生软中断。这种中断是一种需要内核为正在运行的进程去做一些事情(通常为I/O)的请求。

1

2

3

4

5

6

7

具体更详细的解释参考这位大佬的博客:https://www.cnblogs.com/charlesblc/p/6263208.html,讲的很详细

例如在程序中写了 int xx这样的语句,会调用内核中相应的代码,但是它并不中断CPU,我们可以看一下内核中的中断例程,当有中断发生的时候,程序是在IDT中寻找处理函数的,当有中断或异常发生的时候(这里把异常也视为中断的一种),根据中断号码来选择中断处理例程的,寻找的地址是IDT的地址加上8 *中断号。



可以看到上述的一般都是KiTrap函数,x86不使用中断门来处理东西,所以KiTrap系列函数最常用的方式


硬件异常

异常是CPU内部产生的中断,即在CPU执行特定指令的时候出现的非法情况,如除数为0等等,所以不可能在执行指令期间发生异常,只会在执行一条指令后有可能发生,所以也称同步中断。

而中断则是一种异步的,它与特定的进程是无关的。


异常主要分三种,trap(陷入/陷阱,总感觉翻译的怪怪的),fault(错误),abort(终止)。前两种是可恢复的,最后一种不可恢复。trap与fault的最大不同就是发生异常时候保留的EIP不同,trap保留的EIP是发生异常时候的下一条指令的地址,而fault保存的EIP是发生异常的本条指令。举个浅显的例子,fault常见的例如od中我们F2设置普通断点的时候,od会将我们选中的那条指令替换成0xCC,即int 3指令,当执行到这里的时候,cpu会处理这条int 3指令,然后发生一个异常,这个时候就把控制权交给了我们的od,然后od等待用户的操作,当执行的时候,将我们下断点的那个地址重新替换成原本的指令。然后这条指令就相当于“下一条指令”,接着我们单步执行就会执行我们原本的那条指令了。而对于fault的话很常见的就是缺页中断,当发生缺页中断的时候,操作系统会尝试就该页载入内存,然后重新执行我们那条读取/写入页的指令。abort则是为了处理严重的硬件错误等,这类异常不会回复再执行,会强制性退出。


异常和中断会被写进一张叫做IDT的表里,我们可以通过idtr寄存器寻找它,它是一个48位的寄存器,高32位表示IDT的基址,低16位代表长度。IDT里的每一个中断例程都是八字节大小,具体的结构参见我的中断门那一讲。但是IDT里并不只是中断例程,它包括三种不同的描述符(即类似于功能的不同),任务门,中断门,陷阱门。


IDT被设置是在操作系统启动后的,有个名叫Winload的程序会首先cli,屏蔽外部中断,然后利用指令lidt,将信息告诉CPU,接着将控制权交给入口函数,调用sidt保存下来idt的信息,然后其他的处理器也会进行类似的操作,修改和复制idt,最后调用sti来恢复中断。


上述都是硬件异常,


软件异常

软件异常是由操作系统或者应用程序产生的。其主要包含


操作系统事先为我们准备的异常处理过程

我们自己的异常处理(try catch)

这些异常的最终实现都是基于用户态的RaiseException和内核的NtRaiseException建立起来的。

RaiseException的原型如下:


void WINAPI RaiseException(

  _In_       DWORD     dwExceptionCode,

  _In_       DWORD     dwExceptionFlags,

  _In_       DWORD     nNumberOfArguments,

  _In_ const ULONG_PTR *lpArguments

);

1

2

3

4

5

6

ExceptionCode是异常状态码,用于表示是什么原因导致异常,由操作系统指定,也可以由用户程序指定。ExceptionFlag是状态属性


抄一个微软官方的例子看下:


#include "stdafx.h"

#include

#include



DWORD FilterFunction()

{

printf("1 ");                     // printed first 

return EXCEPTION_EXECUTE_HANDLER;

}


BOOL CheckForDebugger()

{

__try

{

DebugBreak();

}

__except (GetExceptionCode() == EXCEPTION_BREAKPOINT ?

EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)

{

// No debugger is attached, so return FALSE 

// and continue.

return FALSE;

}

return TRUE;

}

VOID main(VOID)

{

BOOL bRet = CheckForDebugger();

printf(bRet ? "true" : "false");

}


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

到这里会进行DebugBreak,执行完后会触发一个异常。进入后会引发一个int3断点异常,然后将控制权交给od,然后我们可以选择处理,然后会true,不然根据默认情况,是直接false。


其内部会将异常的相关信息传入一个维护异常的结构,叫做_EXCEPTION_RECORD,然后再去调用RtlRaiseException。并且一般说来,各个异常处理函数除了针对本异常的特殊处理外,通常会将异常信息进行封装。封装的主要有两部分,一部分就是异常记录。


typedef struct _EXCEPTION_RECORD {

  DWORD                    ExceptionCode;

  DWORD                    ExceptionFlags;

  struct _EXCEPTION_RECORD *ExceptionRecord;

  PVOID                    ExceptionAddress;

  DWORD                    NumberParameters;

  ULONG_PTR                ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];

} EXCEPTION_RECORD;

1

2

3

4

5

6

7

8

一部分的ExceptionCode


ExceptionRecord是指向下一个异常的指针。ExceptionRecord是异常发生的地址。NumberParameters是参数的个数。Exception的个数与NumberParameters有关系,最大应不超过EXCEPTION_MAXIMUM_PARAMETERS。


另一部分被封装的内容称为陷阱帧(Trap Frame)。主要记录异常发生时候的线程状态。x86平台是这样定义的。


typedef struct _KTRAP_FRAME

{

//这四项仅为调试系统服务

     ULONG DbgEbp;//拷贝的ebp

     ULONG DbgEip;//拷贝的eip

     ULONG DbgArgMark;

     ULONG DbgArgPointer;


//用于调整栈帧时所需要的

     WORD TempSegCs;

     UCHAR Logging;

     UCHAR Reserved;

     ULONG TempEsp;

//调试寄存器

     ULONG Dr0;

     ULONG Dr1;

     ULONG Dr2;

     ULONG Dr3;

     ULONG Dr6;

     ULONG Dr7;

     

     //段寄存器

     ULONG SegGs;

     ULONG SegEs;

     ULONG SegDs;


//常规寄存器

     ULONG Edx;

     ULONG Ecx;

     ULONG Eax;


//

     ULONG PreviousPreviousMode;//异常发生时所在的层

     PEXCEPTION_REGISTRATION_RECORD ExceptionList;//异常链

     ULONG SegFs;//Fs寄存器


//常规寄存器

     ULONG Edi;

     ULONG Esi;

     ULONG Ebx;

     ULONG Ebp;


//控制寄存器

     ULONG ErrCode;

     ULONG Eip;

     ULONG SegCs;

     ULONG EFlags;


     ULONG HardwareEsp;

     ULONG HardwareSegSs;

     ULONG V86Es;

     ULONG V86Ds;

     ULONG V86Fs;

     ULONG V86Gs;

} KTRAP_FRAME, *PKTRAP_FRAME;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

该结构一般是内核使用的,当将控制权交给用户的时候,会将上述的数据结构,转换成一个名为CONTEXT的结构。


typedef struct _CONTEXT {

            ULONG ContextFlags; //这里是控制flag,与要给用户哪些寄存器有关


//调试寄存器,当ContextFlags包含CONTEXT_DEBUG_REGISTERS

            ULONG   Dr0;

            ULONG   Dr1;

            ULONG   Dr2;

            ULONG   Dr3;

            ULONG   Dr6;

            ULONG   Dr7;

            

            //浮点运算器 当包含CONTEXT_FLOARTING_POINT时有效

            FLOATING_SAVE_AREA FloatSave;


//包含CONTEXT_SEGMENTS时有效

            ULONG   SegGs;

            ULONG   SegFs;

            ULONG   SegEs;

            ULONG   SegDs;


//包含CONTEXT_INTEGERS

            ULONG   Edi;

            ULONG   Esi;

            ULONG   Ebx;

            ULONG   Edx;

            ULONG   Ecx;

            ULONG   Eax;


//包含CONTEXT_CONTROL

            ULONG   Ebp;

            ULONG   Eip;

            ULONG   SegCs;   

            ULONG   EFlags; 

            ULONG   Esp;

            ULONG   SegSs;

//包含CONTEXT_EXTENDED_REGISTERS

            UCHAR   ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];

    } CONTEXT;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

其定义在WinNT头里:


包装完毕后会进一步调用系统内核的异常处理函数KiDispatchException函数来分发处理异常。KiDispatchException是处理的主要核心,其它函数只是对ExceptionRecord和TrapFrame的一种封装然后传给KiDispatchException的过程。

原型如下:


VOID

KiDispatchException (

    IN PEXCEPTION_RECORD ExceptionRecord, //异常处理 封装了本次异常的状态码,flag,指针,参数,地址

    IN PKEXCEPTION_FRAME ExceptionFrame, //总为NULL

    IN PKTRAP_FRAME TrapFrame, //陷阱帧

    IN KPROCESSOR_MODE PreviousMode, //发生异常时CPU处于什么模式

    IN BOOLEAN FirstChance //是否第一次处理异常

    )

1

2

3

4

5

6

7

8

KiDispatchException主要是的功能如下

预处理:

首先设置相应的context_flag,对于用户程序且内核调试被启用,若额外复制一个CONTEXT_FLOATING_POINT的flag

然后用TrapFrame根据flag填充context结构

再分发之前对于STATUS_BREAKPOINT会进行eip-1的处理,然后进行对于不同模式的异常分发。


若处于kernelMode

1.1若是第一次处理该异常,并且存在内核调试器,即KiDebugRoutine存在(此时存在KdpTrap),则将参数分发给KiDebugRoutine,若返回false(也就是内核调试器没有处理该异常),则交给RtlDispatchException处理,RtlDispatchException会试图寻找已注册的SEH,如果处理了返回true,否则返回false继续往下找。

1.2然后系统会给内核调试器第二次机会去处理异常。如果选择了不处理,则会调用KeDebugCheckEx,表明KERNEL_MODE_EXCEPTION_NOT_HANDLED。

若处于userMode

2.1若是第一次处理该异常,并且内核调试器存在,而且进程存在一个调试端口可用,则先分发给内核调试器处理,若不处理发往调试子系统,调试子系统发送给用户态调试器。如果未处理异常则继续。接着首先判断ss段寄存器是否为32位,若不是则没有必要继续,抛出一个STATUS_ACCESS_VIOLATION的异常。若是32位,则继续,判断userStack是否可写,若可写,则将context复制到用户栈上。类似的,会将ExceptionRecord,context的地址,ExceptionRecord的地址依次复制到栈上。然后返回用户层执行执行KiUserExceptionDispatcher,此函数会调用RtlUserExceptionDispatcher,首先遍历VEH,接着遍历SEH(SEH部分包括自己定义的SetUnhandledExceptionFilter,但调试器若存在,是会被交给调试器处理的在链表的最末尾是UnhandledExceptionFilter)。若为处理则继续。

2.2此时是第二次处理异常,尝试再次发给调试端口,然后发往用户态的调试器,若仍然不处理,则将异常发往异常端口,异常端口通常由子系统csrss.exe监听,通常的做法是显示一个“应用程序错误"如果失败则调用ZwTerminateProcess来结束进程

上述过程只要在任何一步被handler或者调试器处理,异常处理就会结束。


帅帅马
6楼 · 2021-04-05 21:25
测试过程:
    操作系统更新到最新的windows10版本,在C#下写一个最简单的抛出异常的程序,成生Debug的64位可执行文件。
    在Windows10下双击运行让它报异常,结果程序没有任何反应,等两秒自动关闭了,没有任何提示。

注:在VisualStudio环境下是能捕获并提示异常的,但现在在Windows下直接运行遇到异常并不捕获和提示,而是直接关闭程序。
722
7楼 · 2021-04-06 14:27

测试过程:
    操作系统更新到最新的windows10版本,在C#下写一个最简单的抛出异常的程序,成生Debug的64位可执行文件。
    在Windows10下双击运行让它报异常,结果程序没有任何反应,等两秒自动关闭了,没有任何提示。

注:在VisualStudio环境下是能捕获并提示异常的,但现在在Windows下直接运行遇到异常并不捕获和提示,而是直接关闭程序。

爱学习的小巴
8楼 · 2021-04-09 15:48

测试过程:操作系统更新到最新的windows10版本,在C#下写一个最简单的抛出异常的程序,成生Debug的64位可执行文件。在Windows10下双击运行让它报异常,结果程序没有任何反应,等两秒自动关闭了,没有任何提示。

寂静的枫林
9楼 · 2021-07-18 18:45

 操作系统更新到最新的windows10版本,在C#下写一个最简单的抛出异常的程序,成生Debug的64位可执行文件。
    在Windows10下双击运行让它报异常,结果程序没有任何反应,等两秒自动关闭了,没有任何提示。

相关问题推荐

  • 回答 2

    Statement的execute(String query)方法用来执行任意的SQL查询,如果查询的结果是一个ResultSet,这个方法就返回true。如果结果不是ResultSet,比如insert或者update查询,它就会返回false。我们可以通过它的getResultSet方法来获取ResultSet,或者通过getUpda...

  • 回答 22

    忙的时候项目期肯定要加班 但是每天加班应该还不至于

  • 回答 108
    已采纳

    虽然Java人才越来越多,但是人才缺口也是很大的,我国对JAVA工程师的需求是所有软件工程师当中需求大的,达到全部需求量的60%-70%,所以Java市场在短时间内不可能饱和。其次,Java市场不断变化,人才需求也会不断增加。马云说过,未来的制造业要的不是石油,...

  • 回答 5
    已采纳

    工信部证书含金量较高。工信部是国务院的下属结构,具有发放资质、证书的资格。其所发放的证书具有较强的权威性,在全国范围内收到认可,含金量通常都比较高。 工信部证书,其含义也就是工信部颁发并承认的某项技能证书,是具有法律效力的,并且是国家认可的...

  • 回答 70
    已采纳

    学Java好不好找工作?看学完Java后能做些什么吧。一、大数据技术Hadoop以及其他大数据处理技术都是用Java或者其他,例如Apache的基于Java 的 HBase和Accumulo以及ElasticSearchas。但是Java在此领域并未占太大空间,但只要Hadoop和ElasticSearchas能够成长壮...

  • 回答 16
    已采纳

    就是java的基础知识啊,比如Java 集合框架;Java 多线程;线程的五种状态;Java 虚拟机;MySQL (InnoDB);Spring 相关;计算机网络;MQ 消息队列诸如此类

  • 回答 12

    #{}和${}这两个语法是为了动态传递参数而存在的,是Mybatis实现动态SQL的基础,总体上他们的作用是一致的(为了动态传参),但是在编译过程、是否自动加单引号、安全性、使用场景等方面有很多不同,下面详细比较两者间的区别:1.#{} 是 占位符 :动态解析 ...

  • 回答 62

    没问题的,专科学历也能学习Java开发的,主要看自己感不感兴趣,只要认真学,市面上的培训机构不少都是零基础课程,能跟得上,或是自己先找些资料学习一下。

  • 回答 4

    1、反射对单例模式的破坏采用反射的方式另辟蹊径实例了该类,导致程序中会存在不止一个实例。解决方案其思想就是采用一个全局变量,来标记是否已经实例化过了,如果已经实例化过了,第 二次实例化的时候,抛出异常2、clone()对单例模式的破坏当需要实现单例的...

  • 回答 5

     优点: 一、实例控制  单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。 二、灵活性  因为类控制了实例化过程,所以类可以灵活更改实例化过程。 缺点: 一、开销  虽然数量很少,但如果每次对象请求引用时都要...

  • 回答 4

    这个主要是看你数组的长度是多少, 比如之前写过的一个程序有个数组存的是各个客户端的ip地址:string clientIp[4]={XXX, xxx, xxx, xxx};这个时候如果想把hash值对应到上面四个地址的话,就应该对4取余,这个时候p就应该为4...

  • 回答 6

     哈希表的大小 · 关键字的分布情况 · 记录的查找频率 1.直接寻址法:取关键字或关键字的某个线性函数值为散列地址。即H(key)=key或H(key) = a·key + b,其中a和b为常数(这种散列函数叫做自身函数)。...

  • 回答 6

    哈希表的大小取决于一组质数,原因是在hash函数中,你要用这些质数来做模运算(%)。而分析发现,如果不是用质数来做模运算的话,很多生活中的数据分布,会集中在某些点上。所以这里最后采用了质数做模的除数。 因为用质数做了模的除数,自然存储空间的大小也用质数了...

  • 回答 2

    是啊,哈希函数的设计至关重要,好的哈希函数会尽可能地保证计算简单和散列地址分布均匀,但是,我们需要清楚的是,数组是一块连续的固定长度的内存空间

  • 回答 3

     解码查表优化算法,seo优化

  • 回答 5

    1.对对象元素中的关键字(对象中的特有数据),进行哈希算法的运算,并得出一个具体的算法值,这个值 称为哈希值。2.哈希值就是这个元素的位置。3.如果哈希值出现冲突,再次判断这个关键字对应的对象是否相同。如果对象相同,就不存储,因为元素重复。如果对象不同,就...

没有解决我的问题,去提问