本文档包括以下内容:
系统异常处理
关于系统异常处理
异常是一个从正常程序运行到一个特殊的过程(称为处理程序handler,即处理此异常)的强制转换。在程序执行过程中,当CPU遇到一个错误时,一个异常就会产生。每个异常被分配一个错误号或是向量号。例如,如果CPU遇到了除以0的情况,那么就会产生0号异常。完整的异常处理讨论请参考:Volume 3: Architecture and Programming of the Pentium® Processor Family Developer's Manual.
重要:
本节所讨论的情况都假设RTSS程序不使用Structured Exception Handling (SEH)。
RTX 处理程序
当RTX处理程序遇到一个错误时,它就开始执行下列行为中的一个:
- 冻结或中止进程
- 修改程序环境,然后继续
- 运行一个有序关机,即著名的“绿屏”。
如果一个异常在ISR(比如一个RTX硬件中断或者RTX定时器中断)中产生,RTX会立即执行一个RTX绿屏停止消息。
RTX 异常处理
对大多数异常,RTX异常处理程序会执行以下任务:
- 冻结或中止出现异常的RTSS进程,这取决于ExceptionDisposition标志的状态
- 通过一个弹出窗口通知用户
- 若在RTX Properties设置了Write Debug Information on System Failure to Minimal Information ,则创建一个RTX minidump文件。
- 创建一个或多个事件日志中相关的项目
1. 冻结或中止出现异常的RTSS进程
注册表项Fault Process ExceptionDisposition(可以在RTX Properties控制面板里设置),控制着出错的RTSS进程的行为,选项有:
- 中止进程(默认)
- 冻结线程和进程
2. 警告用户
通过一个弹出消息框来警告用户。例如,如果异常处理程序中止了进程,RTX可能会显示如下的窗口:
RTX Exception - Memory Access Fault at loc FEBEB229 (Proc=80452448, Thread=804526F ![]() Process image has been * unloaded * |
在这个例子中,一个内存保护失败被检测到。此消息指明了程序指令地址和进程与线程的ID值。线程没有被加载。
如果是冻结进程,RTX可能会显示如下的窗口:
RTX Exception - Memory Access Fault at loc FEBDF229 (Proc=80452448, Thread=804526F ![]() Process image has been * frozen * Debug and/or use "RTSSkill" command to unload all processes |
3. 产生事件日志项目
事件日志消息包括一个信息项目(即弹出窗口的标题部分),一个提供错误详细信息的错误项目。错误项目包含两部分:一个描述和二进制细节。二进制细节由10 DWORD的消息数据和3 DWORD的RTSS失败数据组成。如下所示:
The description for Event ID (12) in Source (Rtx_rtss)
could not be found. It contains the following insertion string(s): /Device/Rtx_rtss, RTX Exception - Memory Access Fault 0000: 00100000 00620002 00000000 c004000c 0010: 00000000 c0000183 00000000 00000000 0020: 00000000 00000000 feac12e8 febdf229 0030: 80452448 804526f8 |
头10个dwords是唯一的。3dwords的RTSS失败数据为:
- 指令指针值(上例中为febdf229)
- RTSS进程标识(上例中为80452448)
- RTSS线程标识(上例中为804526f8)
注意:为保护事件日志信息,不要过滤事件项目。
异常代码
包括RTX异常代码和Intel异常代码
1. RTX异常代码
Exception | Error code | RTX will... |
EXCEPTION_NPX_NOT_AVAILABLE | 0X07 | Allow for floating-point capability, and continue executing the application. |
EXCEPTION_INT3 | 0x03 | Enter the debugger. If no debugger is present, RTX freezes the process. |
EXCEPTION_NMI EXCEPTION_DOUBLE_FAULT EXCEPTION_RESERVED_TRAP EXCEPTION_INVALID_TSS | 0x02 0x08 0x0F 0x0A | Issue a Green Screen stop message. |
2. Intel异常代码
x86 Exception | Win32 Mapping |
EXCEPTION_DIVIDED_BY_ZERO | EXCEPTION_INT_DIVIDE_BY_ZERO EXCEPTION_FLT_DIVIDE_BY_ZERO |
EXCEPTION_NPX_OVERRUN | EXCEPTION_FLT_INVALID_OPERATION |
EXCEPTION_NPX_ERROR | EXCEPTION_FLT_DENORMAL_OPERAND EXCEPTION_FLT_DIVIDE_BY_ZERO EXCEPTION_FLT_OVERFLOW EXCEPTION_FLT_UNDERFLOW EXCEPTION_FLT_INEXACT_RESULT EXCEPTION_FLT_INVALID_OPERATION |
EXCEPTION_INVALID_OPCODE | EXCEPTION_ILLEGAL_INSTRUCTION |
EXCEPTION_GP_FAULT EXCEPTION_PAGE_FAULT EXCEPTION_BOUND_CHECK EXCEPTION_SEGMENT_NOT_PRESENT | EXCEPTION_ACCESS_VIOLATION |
EXCEPTION_DEBUG | EXCEPTION_BREAKPOINT |
EXCEPTION_ALIGNMENT_CHECK | EXCEPTION_DATATYPE_MISALIGNMENT |
RTX与Windows停止消息
- 蓝屏和停止消息处理
- 关机处理
- 使能停止消息
- Windows停止屏幕说明
- RTX绿屏停止消息说明
1. 蓝屏和停止消息处理
通过与Windows共享同一个系统,RTX允许开发者利用Windows的那些不是严格实时任务的组件和服务。甚至蓝屏(或者Windows停止运行消息)之后,RTX子系统仍能继续运行,此时构建在RTX或者RT-TCP/IP的任何程序或驱动也都能继续运行。
A. 什么是蓝屏?
Windows蓝屏或停止消息是系统失败后的一种智能检查和调用KeBugCheck使Windows返回一个可控停止的状态。此bug-check将数据毁坏保持到最小,并帮助找出系统出问题的原因。所有复杂的操作系统都提供了这种检查和停止功能。实时操作系统(RTOS)一般不做可控的紧急停止,因为它认为保持硬件在任何可能情况下继续运行更为重要。
Windows调用KeBugCheck有诸多原因,如磁盘失败或者调用了坏指针(bad pointers)。因为能捕获到的失败一般都不是完全硬件失败,如CPU失去控制(这种情况很少发生),RTX甚至会在调用KeBugCheck之后继续运行。所有RTSS进程继续执行实时任务,但是此时也只能执行实时任务。因为Windows提供的服务和组件都不再存在,继续运行的RTSS进程就不能在Windows上调用了。
B. Windows发生了什么?
KeBugCheck调用事件中:
- 调度器立刻停止
- 从GUI模式切换到字符模式
- 一个多处理器系统上的所有非启动处理器停止运行
- 注册过bug-check提示的驱动得到事件通知
- Windows转储内存(可选)
- Windows重启(可选)
C. 设置蓝屏后RTX继续运行
目标为将设备重新置于安全的状态,或者允许实时程序在Windows进行bug-check之后继续运行一段合理的时间
- 蓝屏前一个关闭句柄会连接到每个必须要继续运行的进程上。每个进程只被连接一个关闭句柄,并且它被创建为一个暂停的线程(suspended thread),这个线程在蓝屏发生时会被释放掉(be released)。关闭句柄的实体一般包含关闭动作发生时警告其进程和线程的代码,而主进程和其线程继续处理大量的运算。
- 一旦关闭动作发生,RTX会拦截bug check。RTX重新设计了字符模式的显示,然后RTSS环境进行接管。
- 一旦所有拥有关闭句柄的进程的句柄进行了卸载,正常的系统关闭会继续发生。你可以按需要自动重启Windows。
D. 蓝屏后确定性的API工作
在一个实时系统中,重要的任务由RTX来处理,监视及其他任务由Windows来处理。蓝屏发生后再调用Windows时会导致调用线程或进程挂起,因为Windows已经不再能响应请求。必须要小心调用RTX不能直接处理的API函数。一般地,确定性的API会在蓝屏之后工作。然后,有一些非确定性的函数被设计成可以在蓝屏后工作,如printf。当向你的应用程序和驱动中添加关闭程序(shutdown handler)时,请依照以下方法:
- 在RTX帮助文档索引中,在“Matrixes”条目下有四个函数表格。里面标记为Deterministic可以工作。
- 一些非确定性的函数也能工作,如printf,RtPrintf,RtWPrintf。
- 分配内存的函数不能工作,如calloc和malloc。
- 创建东西的函数不能工作,包括创建线程、定时器、对象和变量。所有初始化工作应该在实时处理开始之前就做完,并且重要的初始化步骤失败时应调用RtReleaseShutdownHandler。如果在关闭时需要一些新的线程,你可以事先创建一些线程,让它们处在以下状态:暂停、等待事件或由关闭函数(shutdown handler)触发的全局变量的变化。
- 使用Windows的函数不能工作。往一个进程中添加使用RTX和Windows的关机程序是危险的,因为调用Windows的任何操作都会挂起进程或线程。
- 实时打开(如RtOpenEvent)调用一般能工作,但极少情况下有可能会失败,故最好是避免调用它们。
E. 文件系统
蓝屏后,FAT/FAT32会要求磁盘检查,NTFS分区不要求。
F. RTX场景
在每一个需要蓝屏后运行的进程中添加关机处理函数。由此操作创建的进程和所有线程将会在蓝屏后继续工作。
RTX一般会在进程最开始时,即时序严格要求的处理开始之前,执行所有初始化,内存分配和非确定性操作。这一点在使用关机处理函数之前尤为重要,因为蓝屏后,非确定性函数都不能使用(并会导致线程/进程挂起)。你可以设计检测系统状态的程序,在蓝屏发生时通过发一个事件、改变一个变量或者类似技术来警告它们。在关机处理函数返回前主进程不应结束。因为这会阻止关机处理函数返回和挂起进程。
main()
{
BOOL bBlueScreen = FALSE;
RtCreateEvent()…
CreateThread()…
RtAttachShutdownHandler()…
Sleep(INFINITE);
// until shutdown handler returns
}
正常关机或蓝屏后,关机处理函数开始执行。一般地,关机发生时,此线程的实体会警告此进程和它的线程,它们的行为也随之改变。当需要的处理结束后,关机处理函数返回。这里,RtPulseEvent会被调用,以通知任何睡眠的线程或其他程序。例程rtxtcpserver改变了一个全局变量的值来阻止创建线程的程序。当关机处理函数返回时,相关的进程和其所有线程将会中止。你必须引进一些机制,来确保直到处理结束时关机处理函数一直打开。
ShutdownHandler()
{
// when shutdown occurs, pulse event
RtPulseEvent()
// or set a global variable
bBlueScreen = TRUE;
// continue other processing, but keep ShutdownHandler open
RtWaitForSingleObject()
}
此线程等待在关机处理函数中一个事件的触发。尽管蓝屏后不能创建线程,但是你可以创建蓝屏前不工作的线程。
Thread1()
{
// wait until event is triggered
RtWaitForSingleObject()
// shutdown occurred, begin cleanup
}
G. 错误检查和清理
在RTX关机模式并不需要(并不可能)关闭句柄或销毁对象。因为不能再分配内存,这些对象使用的内存也不需要了,所以这部分的清理是不必要的。但是必须注意的是不能向Windows请求资源,因为操作系统已经不可用,任何调用都将导致线程/进程挂起。
为了清理,你可能需要像下面的代码一样放置一些销毁、关闭句柄和退出函数:
if (bBlueScreen==TRUE)
Sleep(INFINITE); // no destructors or process exits allowed
H. 系统初始化
系统初始化过程中,如果一个重要的部分不能被初始化,可以通过调用RtReleaseShutdownHandler来释放关机处理函数:
if (!(hTimer = RtCreateTimer( NULL, stackSize, default TimerHandler, NULL, RT_PRIORITY_MAX, CLOCK_FASTEST)))
{
printf("Shutdown: Error: Could not create the timer. GetLastError = %d/n", GetLastError());
RtReleaseShutdownHandler(hShutdown);
return ERROR_OCCURRED;
}
2. 关机处理
RTX关机处理机制为在Windows紧急蓝屏崩溃或RTSS之外“正常”关闭时产生的Windows突然死亡情况下的实时设备设计。当Windows突然死亡时,RTX推迟了其死亡过程,并切换至RTX关机模式,以允许RTSS程序做一些紧急的清理调用。在RTX关机处理过程中,RTX子系统仍可操作并且全部功能可用;Win32 RTX线程则不能。在RTX关闭时,只有通过调用RtAttachShutdownhandler的RTSS进程才有功能。我们将这些进程命令为关机进程(shutdown processes)。在RTX关机周期中没有时限,可以达到永远(但此时Windows将不能执行它自己的关机程序了)。
在RTX关机模式下,关机处理与其他进程有所不同。在Windows关机期间,只有这些RTSS进程保持功能,所有其他的都被冻结。每一个关机处理函数(由RtReleaseShutdownhandler()连接),在单独的RTSS线程上下文中作用,并只有在RTX 关机期间激活。在正常的Windows和RTX操作时,这些RTX关机线程都处在休眠状态。只有一个关机处理函数可以被连接到RTSS进程。对同一个程序的RtReleaseShutdownhandler() 第二次调用将被忽略,除非这之间调用了RtReleaseShutdownhandler()。
关机处理线程开始自调用使用RtAttachShutdownhandler()连接的关机处理函数。此函数有两个参数,第一个参数由用户在调用RtAttachShutdownhandler时定义,第二个参数由RTX提供,用来解释当前关机的原因,以帮助关机处理函数动态地检测Windows出了什么问题。
关机处理线程不是唯一在Windows关机期间活跃的RTSS关机处理进程。所有其他的RTSS关机处理线程也活跃,并且并不限制于普通RTSS实时表现(不需要Windows的地方)。然而,取决于Windows关机的类型,它们可以被限制为所有或部分非实时调用。Windows只会在所有RTSS关机进程结束后才能继续关机。一旦最后一个RTSS关机处理进程退出,RTSS就会停止或者卸载,然后Windows会继续进行关机进程。
在RTX关机期间,RTSS关机处理进程可以跟其他线程一样完全控制它的关机处理线程--可以延迟其动作,甚至可以改变其优先级(这样做并不明智),但是它不可以创建新的RTSS线程和对象。可以在关机处理线程上下文中调用GetCurrentThread() 来获取关机处理线程的句柄。唯一的区别是,如果关机处理线程存在或者被中止(因为“return”,调用了ExitThread() 或者 TerminateThread()),整个关机处理进程仍然存在(就像它是主线程一样)。
Windows 蓝屏关机
所有关机处理线程被激活,另外还有一个额外的线程:关机线程。RTS定时器工作,所有线程可以执行实时调用。但是,它们不能调用任何非实时函数(需要Windows服务),因为当Windows出现致命损坏并将要崩溃之时只有RTX子系统能够工作。唯一的例外是调用RtPrintf() 和 printf() ,这二者在RTX关机模式时将在蓝屏上直接打印信息,但是打印的区域仅限于蓝屏的大小。
在切换到关机模式之前,RTX有选择的将屏幕重置为字符模式,这取决于Shutdown Print Handling 设置。这个重置可能会消耗时间,甚至如果RTSS工作在同一个处理器时有可能不是一个实时调用(在重置时可能会屏蔽中断)。这通常发生于单一处理器的系统上,有时会出现在多处理器系统中的共享模式中。
Windows正常关机
在此阶段不建议创建新的RTSS线程和对象,因为Windows已经关闭了一些服务。
3. 使能停止消息
RTX绿屏或者Windows蓝屏上的停止消息可以迅速提供一个系统崩溃的关键信息。为了使能停止消息,你必须关闭Windows自动重启选项:
a. 系统属性->高级
b. 启动和故障恢复->设置
c. 系统失败->自动重启系统,不勾选。
4. 解读Windows停止屏幕
在Windows上,经典的“蓝屏”已经被淘汰。用户仍然可以使用“minidump”或者小内存转储选项来获得相同类型的信息。二者都需要在系统盘有2MB的页面文件,并进行相关的配置。
这种转储文件包含了以下信息:
停止信息和参数及其他数据
加载的驱动列表
- 停止运行的处理器的processor context (PRCB)
- 进程信息和停止工作的进程的内核内容(EPROCESS)
- 进程信息和停止工作的线程的内核内容(ETHREAD)
- 停止工作的线程的内核态调用堆(Kernel-mode call stack)
5. 解读RTX绿屏停止消息
当RTSS程序发生不可恢复的异常时,RTX异常处理程序会产生一个绿屏。RTX绿屏主要包含四段数据:技术信息,线程上下文和当前线程堆这三个是RTX特有的,而最后一段刚类似于Windows蓝屏时的信息。
1. 技术信息段
此段数据提供了:
- bug自检码:一个表明错误源的16进制数
- 当前RTSS进程名称与ID
- 当前RTSS线程ID与地址
- 异常地址
2. 线程上下文
包含线程上下文信息
3. 当前线程堆
包含最后活动的RTSS线程,包括线程标识,进程名称,进程标识,指标线程行为的标志,当前状态,原始优先级和当前优先级。
4. 解读蓝屏消息
停止消息段提供了如下bug检查数据:
*** STOP: #E(#1, #2, #3, #4)
*** Address XXXXXXXX has base at YYYYYYYY-Kernel Image |
其中
#E = 停止代码.
#1 = identifies the address that was referenced improperly.
#2 = identifies the IRQL that was required to access the memory.
#3 = identifies the access as a Write (Read = 0).
#4 = identifies the instruction address that attempted to access the memory that was referenced in the first parameter
Text = The text interpretation attempts to match one of the entries in the stop message to a loaded image base address to determine approximately where the fault was raised.