2021-03-30 20:02发布
测试过程: 操作系统更新到最新的windows10版本,在C#下写一个最简单的抛出异常的程序,成生Debug的64位可执行文件。 在Windows10下双击运行让它报异常,结果程序没有任何反应,等两秒自动关闭了,没有任何提示。注:在VisualStudio环境下是能捕获并提示异常的,但现在在Windows下直接运行遇到异常并不捕获和提示,而是直接关闭程序。
RuntimeException可以不catch,出错会自动向上抛出,直到最后都没有catch就jvm异常退出所以异常应该继承自RuntimeException,否则在方法定义的时候抛出了异常,在调用方法时就必须catch,除非调用方法的方法也是定义为抛出异常
中断
首先讲下硬中断和软中断的区别
硬中断:硬中断是由硬件产生的,比如,像磁盘,网卡,键盘,时钟等。每个设备或设备集都有它自己的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
);
ExceptionCode是异常状态码,用于表示是什么原因导致异常,由操作系统指定,也可以由用户程序指定。ExceptionFlag是状态属性
抄一个微软官方的例子看下:
#include "stdafx.h"
#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");
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;
一部分的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;
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
//浮点运算器 当包含CONTEXT_FLOARTING_POINT时有效
FLOATING_SAVE_AREA FloatSave;
//包含CONTEXT_SEGMENTS时有效
ULONG SegFs;
//包含CONTEXT_INTEGERS
//包含CONTEXT_CONTROL
ULONG Esp;
ULONG SegSs;
//包含CONTEXT_EXTENDED_REGISTERS
UCHAR ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
} CONTEXT;
其定义在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 //是否第一次处理异常
)
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或者调试器处理,异常处理就会结束。
测试过程:操作系统更新到最新的windows10版本,在C#下写一个最简单的抛出异常的程序,成生Debug的64位可执行文件。在Windows10下双击运行让它报异常,结果程序没有任何反应,等两秒自动关闭了,没有任何提示。
操作系统更新到最新的windows10版本,在C#下写一个最简单的抛出异常的程序,成生Debug的64位可执行文件。 在Windows10下双击运行让它报异常,结果程序没有任何反应,等两秒自动关闭了,没有任何提示。
Statement的execute(String query)方法用来执行任意的SQL查询,如果查询的结果是一个ResultSet,这个方法就返回true。如果结果不是ResultSet,比如insert或者update查询,它就会返回false。我们可以通过它的getResultSet方法来获取ResultSet,或者通过getUpda...
忙的时候项目期肯定要加班 但是每天加班应该还不至于
虽然Java人才越来越多,但是人才缺口也是很大的,我国对JAVA工程师的需求是所有软件工程师当中需求大的,达到全部需求量的60%-70%,所以Java市场在短时间内不可能饱和。其次,Java市场不断变化,人才需求也会不断增加。马云说过,未来的制造业要的不是石油,...
工信部证书含金量较高。工信部是国务院的下属结构,具有发放资质、证书的资格。其所发放的证书具有较强的权威性,在全国范围内收到认可,含金量通常都比较高。 工信部证书,其含义也就是工信部颁发并承认的某项技能证书,是具有法律效力的,并且是国家认可的...
学Java好不好找工作?看学完Java后能做些什么吧。一、大数据技术Hadoop以及其他大数据处理技术都是用Java或者其他,例如Apache的基于Java 的 HBase和Accumulo以及ElasticSearchas。但是Java在此领域并未占太大空间,但只要Hadoop和ElasticSearchas能够成长壮...
就是java的基础知识啊,比如Java 集合框架;Java 多线程;线程的五种状态;Java 虚拟机;MySQL (InnoDB);Spring 相关;计算机网络;MQ 消息队列诸如此类
#{}和${}这两个语法是为了动态传递参数而存在的,是Mybatis实现动态SQL的基础,总体上他们的作用是一致的(为了动态传参),但是在编译过程、是否自动加单引号、安全性、使用场景等方面有很多不同,下面详细比较两者间的区别:1.#{} 是 占位符 :动态解析 ...
没问题的,专科学历也能学习Java开发的,主要看自己感不感兴趣,只要认真学,市面上的培训机构不少都是零基础课程,能跟得上,或是自己先找些资料学习一下。
1、反射对单例模式的破坏采用反射的方式另辟蹊径实例了该类,导致程序中会存在不止一个实例。解决方案其思想就是采用一个全局变量,来标记是否已经实例化过了,如果已经实例化过了,第 二次实例化的时候,抛出异常2、clone()对单例模式的破坏当需要实现单例的...
优点: 一、实例控制 单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。 二、灵活性 因为类控制了实例化过程,所以类可以灵活更改实例化过程。 缺点: 一、开销 虽然数量很少,但如果每次对象请求引用时都要...
这个主要是看你数组的长度是多少, 比如之前写过的一个程序有个数组存的是各个客户端的ip地址:string clientIp[4]={XXX, xxx, xxx, xxx};这个时候如果想把hash值对应到上面四个地址的话,就应该对4取余,这个时候p就应该为4...
哈希表的大小 · 关键字的分布情况 · 记录的查找频率 1.直接寻址法:取关键字或关键字的某个线性函数值为散列地址。即H(key)=key或H(key) = a·key + b,其中a和b为常数(这种散列函数叫做自身函数)。...
哈希表的大小取决于一组质数,原因是在hash函数中,你要用这些质数来做模运算(%)。而分析发现,如果不是用质数来做模运算的话,很多生活中的数据分布,会集中在某些点上。所以这里最后采用了质数做模的除数。 因为用质数做了模的除数,自然存储空间的大小也用质数了...
是啊,哈希函数的设计至关重要,好的哈希函数会尽可能地保证计算简单和散列地址分布均匀,但是,我们需要清楚的是,数组是一块连续的固定长度的内存空间
解码查表优化算法,seo优化
1.对对象元素中的关键字(对象中的特有数据),进行哈希算法的运算,并得出一个具体的算法值,这个值 称为哈希值。2.哈希值就是这个元素的位置。3.如果哈希值出现冲突,再次判断这个关键字对应的对象是否相同。如果对象相同,就不存储,因为元素重复。如果对象不同,就...
最多设置5个标签!
测试过程:
操作系统更新到最新的windows10版本,在C#下写一个最简单的抛出异常的程序,成生Debug的64位可执行文件。
在Windows10下双击运行让它报异常,结果程序没有任何反应,等两秒自动关闭了,没有任何提示。
注:在VisualStudio环境下是能捕获并提示异常的,但现在在Windows下直接运行遇到异常并不捕获和提示,而是直接关闭程序。
RuntimeException可以不catch,出错会自动向上抛出,直到最后都没有catch就jvm异常退出
所以异常应该继承自RuntimeException,否则在方法定义的时候抛出了异常,在调用方法时就必须catch,除非调用方法的方法也是定义为抛出异常
RuntimeException可以不catch,出错会自动向上抛出,直到最后都没有catch就jvm异常退出
所以异常应该继承自RuntimeException,否则在方法定义的时候抛出了异常,在调用方法时就必须catch,除非调用方法的方法也是定义为抛出异常
中断
首先讲下硬中断和软中断的区别
硬中断:硬中断是由硬件产生的,比如,像磁盘,网卡,键盘,时钟等。每个设备或设备集都有它自己的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或者调试器处理,异常处理就会结束。
操作系统更新到最新的windows10版本,在C#下写一个最简单的抛出异常的程序,成生Debug的64位可执行文件。
在Windows10下双击运行让它报异常,结果程序没有任何反应,等两秒自动关闭了,没有任何提示。
注:在VisualStudio环境下是能捕获并提示异常的,但现在在Windows下直接运行遇到异常并不捕获和提示,而是直接关闭程序。
测试过程:
操作系统更新到最新的windows10版本,在C#下写一个最简单的抛出异常的程序,成生Debug的64位可执行文件。
在Windows10下双击运行让它报异常,结果程序没有任何反应,等两秒自动关闭了,没有任何提示。
注:在VisualStudio环境下是能捕获并提示异常的,但现在在Windows下直接运行遇到异常并不捕获和提示,而是直接关闭程序。
测试过程:操作系统更新到最新的windows10版本,在C#下写一个最简单的抛出异常的程序,成生Debug的64位可执行文件。在Windows10下双击运行让它报异常,结果程序没有任何反应,等两秒自动关闭了,没有任何提示。
操作系统更新到最新的windows10版本,在C#下写一个最简单的抛出异常的程序,成生Debug的64位可执行文件。
在Windows10下双击运行让它报异常,结果程序没有任何反应,等两秒自动关闭了,没有任何提示。
相关问题推荐
Statement的execute(String query)方法用来执行任意的SQL查询,如果查询的结果是一个ResultSet,这个方法就返回true。如果结果不是ResultSet,比如insert或者update查询,它就会返回false。我们可以通过它的getResultSet方法来获取ResultSet,或者通过getUpda...
忙的时候项目期肯定要加班 但是每天加班应该还不至于
虽然Java人才越来越多,但是人才缺口也是很大的,我国对JAVA工程师的需求是所有软件工程师当中需求大的,达到全部需求量的60%-70%,所以Java市场在短时间内不可能饱和。其次,Java市场不断变化,人才需求也会不断增加。马云说过,未来的制造业要的不是石油,...
工信部证书含金量较高。工信部是国务院的下属结构,具有发放资质、证书的资格。其所发放的证书具有较强的权威性,在全国范围内收到认可,含金量通常都比较高。 工信部证书,其含义也就是工信部颁发并承认的某项技能证书,是具有法律效力的,并且是国家认可的...
学Java好不好找工作?看学完Java后能做些什么吧。一、大数据技术Hadoop以及其他大数据处理技术都是用Java或者其他,例如Apache的基于Java 的 HBase和Accumulo以及ElasticSearchas。但是Java在此领域并未占太大空间,但只要Hadoop和ElasticSearchas能够成长壮...
就是java的基础知识啊,比如Java 集合框架;Java 多线程;线程的五种状态;Java 虚拟机;MySQL (InnoDB);Spring 相关;计算机网络;MQ 消息队列诸如此类
#{}和${}这两个语法是为了动态传递参数而存在的,是Mybatis实现动态SQL的基础,总体上他们的作用是一致的(为了动态传参),但是在编译过程、是否自动加单引号、安全性、使用场景等方面有很多不同,下面详细比较两者间的区别:1.#{} 是 占位符 :动态解析 ...
没问题的,专科学历也能学习Java开发的,主要看自己感不感兴趣,只要认真学,市面上的培训机构不少都是零基础课程,能跟得上,或是自己先找些资料学习一下。
1、反射对单例模式的破坏采用反射的方式另辟蹊径实例了该类,导致程序中会存在不止一个实例。解决方案其思想就是采用一个全局变量,来标记是否已经实例化过了,如果已经实例化过了,第 二次实例化的时候,抛出异常2、clone()对单例模式的破坏当需要实现单例的...
优点: 一、实例控制 单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。 二、灵活性 因为类控制了实例化过程,所以类可以灵活更改实例化过程。 缺点: 一、开销 虽然数量很少,但如果每次对象请求引用时都要...
这个主要是看你数组的长度是多少, 比如之前写过的一个程序有个数组存的是各个客户端的ip地址:string clientIp[4]={XXX, xxx, xxx, xxx};这个时候如果想把hash值对应到上面四个地址的话,就应该对4取余,这个时候p就应该为4...
哈希表的大小 · 关键字的分布情况 · 记录的查找频率 1.直接寻址法:取关键字或关键字的某个线性函数值为散列地址。即H(key)=key或H(key) = a·key + b,其中a和b为常数(这种散列函数叫做自身函数)。...
哈希表的大小取决于一组质数,原因是在hash函数中,你要用这些质数来做模运算(%)。而分析发现,如果不是用质数来做模运算的话,很多生活中的数据分布,会集中在某些点上。所以这里最后采用了质数做模的除数。 因为用质数做了模的除数,自然存储空间的大小也用质数了...
是啊,哈希函数的设计至关重要,好的哈希函数会尽可能地保证计算简单和散列地址分布均匀,但是,我们需要清楚的是,数组是一块连续的固定长度的内存空间
解码查表优化算法,seo优化
1.对对象元素中的关键字(对象中的特有数据),进行哈希算法的运算,并得出一个具体的算法值,这个值 称为哈希值。2.哈希值就是这个元素的位置。3.如果哈希值出现冲突,再次判断这个关键字对应的对象是否相同。如果对象相同,就不存储,因为元素重复。如果对象不同,就...