如果堆栈跨越内核边界,为什么 64 位 Windows 不能在异常期间展开堆栈 - 而 32 位 Windows 可以?
整个问题的上下文来自:
The case of the disappearing OnLoad exception – user-mode callback exceptions in x64
在 32 位 Windows 中,如果我在我的用户模式代码中抛出一个异常,它是从内核模式代码中回调的,它是从我的中调用的>用户模式代码,例如:
User mode Kernel Mode
------------------ -------------------
CreateWindow(...); ------> NtCreateWindow(...)
|
WindowProc <---------------------+
Windows 中的结构化异常处理 (SEH) 可以展开堆栈,通过内核模式展开,回到我的用户代码,我可以在其中处理异常并看到有效的堆栈跟踪。
64 位版本的 Windows 无法执行此操作:
For complicated reasons, we cannot propagate the exception back on 64-bit operating systems (amd64 and IA64). This has been the case ever since the first 64-bit release of Server 2003. On x86, this isn’t the case – the exception gets propagated through the kernel boundary and would end up walking the frames back
并且由于在这种情况下无法返回可靠的堆栈跟踪,因此必须做出决定:让您看到非无意义的异常,或者完全隐藏它:
The kernel architects at the time decided to take the conservative AppCompat-friendly approach – hide the exception, and hope for the best.
文章接着讨论了所有 64 位 Windows 操作系统的行为方式:
但是从 Windows 7(和 Windows Server 2008)开始,架构师改变了他们的想法 - 有点。对于仅 64 位应用程序(不是 32 位应用程序),它们将(默认情况下)停止抑制这些用户-内核-用户异常.因此,默认情况下,打开:
所有 64 位应用程序都将看到这些异常,而它们以前从未看到过。
In Windows 7, when a native x64 application crashes in this fashion, the Program Compatibility Assistant is notified. If the application doesn’t have a Windows 7 Manifest, we show a dialog telling you that PCA has applied an Application Compatibility shim. What does this mean? This means, that the next time you run your application, Windows will emulate the Server 2003 behavior and make the exception disappear. Keep in mind, that PCA doesn’t exist on Server 2008 R2, so this advice doesn’t apply.
问题是为什么 64 位 Windows 无法通过内核转换展开堆栈,而 32 位版本的 Windows 可以?
唯一的提示是:
For complicated reasons, we cannot propagate the exception back on 64-bit operating systems (amd64 and IA64).
提示是它很复杂。
我可能不明白解释,因为我不是操作系统开发人员 - 但我想知道为什么。
Microsoft 已发布 a hotfix enables 32-bit applications也不再抑制异常:
KB976038: Exceptions that are thrown from an application that runs in a 64-bit version of Windows are ignored
- An exception that is thrown in a callback routine runs in the user mode.
In this scenario, this exception does not cause the application to crash. Instead, the application enters into an inconsistent state. Then, the application throws a different exception and crashes.
A user mode callback function is typically an application-defined function that is called by a kernel mode component. Examples of user mode callback functions are Windows procedures and hook procedures. These functions are called by Windows to process Windows messages or to process Windows hook events.
此修补程序随后可让您阻止 Windows 在全局范围内处理异常:
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options DisableUserModeCallbackFilter: DWORD = 1
或每个应用程序:
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\Notepad.exe DisableUserModeCallbackFilter: DWORD = 1
KB973460 中还记录了 XP 和 Server 2003 上的行为:
我在调查使用 xperf 在 64 位 Windows 上捕获堆栈跟踪时发现了另一个提示:
Disable Paging Executive
In order for tracing to work on 64-bit Windows you need to set the DisablePagingExecutive registry key. This tells the operating system not to page kernel mode drivers and system code to disk, which is a prerequisite for getting 64-bit call stacks using xperf, because 64-bit stack walking depends on metadata in the executable images, and in some situations the xperf stack walk code is not allowed to touch paged out pages. Running the following command from an elevated command prompt will set this registry key for you.
REG ADD "HKLM\System\CurrentControlSet\Control\Session Manager\Memory Management" -v DisablePagingExecutive -d 0x1 -t REG_DWORD -fAfter setting this registry key you will need to reboot your system before you can record call stacks. Having this flag set means that the Windows kernel locks more pages into RAM, so this will probably consume about 10 MB of additional physical memory.
这给人的印象是在 64 位 Windows 中(并且仅在 64 位 Windows 中),不允许遍历内核堆栈,因为磁盘上可能有页面。
最佳答案
我是很久以前编写此修补程序以及博客文章的开发人员。主要原因是出于性能原因,当您转换到内核空间时,并不总是捕获完整的寄存器文件。
如果您进行正常的系统调用,x64 Application Binary Interface (ABI) 只需要你保留 the non-volatile registers (类似于进行正常的函数调用)。但是,正确展开异常需要您拥有所有 寄存器,因此这是不可能的。基本上,这是在关键场景(即每秒可能发生数千次的场景)的性能与 100% 正确处理病态场景(崩溃)之间的选择。
关于windows - 为什么 64 位 Windows 不能取消用户-内核-用户异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11376795/
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返
它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput
我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串
如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象