微软技术专家谈x86架构的怪异之处

2022 年 4 月 21 日 CSDN


【CSDN 编者按】1978年6月8日,Intel发布了新款16位微处理器“8086”,也同时开创了一个新时代:x86架构诞生了。x86指的是特定微处理器执行的一些计算机语言指令集,定义了芯片的基本使用规则,一如今天的x64、IA64等。它不仅成就了Intel如日中天的地位,也成为了一种业界标准,即使是在当今强大的多核心处理器上也能看到x86的身影。

微软技术专家Raymond Chen参与Windows开发已经25年了,在实际的操作中,他认为X86构架有很多奇怪之处。

原文地址:https://devblogs.microsoft.com/oldnewthing/20220418-00/?p=106489

本文由CSDN翻译,转载需注明来源出处。

译者 | 章雨铭        责编 | 张红月
出品 | CSDN(ID:CSDNnews)

以下为译文:

最近,我发现x86架构还有一个和其他的架构不同的地方——Windows结构化异常的管理方式。

在Windows上,所有其他架构都是通过使用unwind代码和声明为元数据的其他信息来跟踪异常处理。如果在其他架构上单步执行一个函数,就不会看到与异常处理相关的任何指令。只有在发生异常时,系统才会在元数据中的异常处理信息中查找指令指针,并使用它来决定要执行的操作:应该运行哪个异常处理程序?哪些对象需要销毁?和其他诸如此类的问题。

但奇怪的是,在Windows上,x86在运行时跟踪异常信息。当控制进入一个需要处理异常的函数时(要么是因为它想要处理异常,要么只是因为它想在异常被抛出函数时运行析构函数),代码必须在一个通过堆栈的链接列表中创建一个条目,并以.NET中的值为锚。在Microsoft Visual C++的实现中,链接列表节点还包含一个整数,代表当前函数的进度,每当需要销毁的对象列表发生变化时,该整数就会被更新。它在一个对象的构建完成后立即更新,并在对象的销毁开始前立即更新。fs:[0]

这个特殊的整数是一个非常麻烦的问题,因为优化器视其为废弃储存,想把它优化掉。的确,有时它确实是废弃储存,但有时它不是。

 

struct S { S(); ~S(); };

void f1();void f2();

S g(){ S s1; f1(); S s2; f2(); return S();}

此函数的代码生成过程如下:

struct ExceptionNode{    ExceptionNode* next;    int (__stdcall *handler)(PEXCEPTION_POINTERS);    int state;};

S g(){ // Create a new node ExceptionNode node; node.next = fs:[0]; node.handler = exception_handler_function; node.state = -1; // nothing needs to be destructed

// Make it the new head of the linked list fs:[0] = &node;

construct s1; node.state = 0; // s1 needs to be destructed

f1();

construct s2; node.state = 1; // s1 and s2 need to be destructed

f2();

construct return value; node.state = 2; // s1, s2, and return value need to be destructed

node.state = 3; // s1 and return value need to be destructed destruct s2;

node.state = 4; // return value needs to be destructed destruct s1;}

 

 

每当 "需要销毁的对象 "的列表发生变化时,就会更新unwind状态变量。就优化器而言,所有这些更新看起来都是废弃储存,因为似乎没有人读它们。.state

但确实有人读它们:.the。问题是,对the的调用是不可见的。当一个异常被或函数抛出时,它被调用,或者被对象的析构器调用。

但是,其中有些真的是废弃储存。例如,2的赋值是一个废弃储存,因为它后面紧跟着3的存储,中间没有任何东西,所以当值是2的时候,不会有异常发生。同样,3的存储是废弃的,因为3的析构器是隐含的。当破坏.node.stateSnoexcepts1时,不可能发生异常。 

如果或改为.f1f2noexcept,废弃储存就可能被消除。

因此,优化器进退两难。它想消除废弃储存,但识别废弃储存的简单算法在这里不起作用,因为有可能出现异常。

Coroutine使情况变得更糟:当一个coroutine暂停时,异常处理节点需要从堆栈中复制到coroutine框架中,然后从堆栈框架中删除。而当协程恢复时,状态需要从协程框架复制回堆栈,并链接到异常处理程序链中。

确切地知道何时执行此操作取消链接和重新链接是很困难的,因为你仍然必须捕获其中发生的异常,并把它们存储在promise中。但这很可能不可行,因为在返回之前,coroutine可能已经恢复并运行到完成。

.await_suspendawait_suspendawait_suspend

void await_suspend(coroutine_handle<> handle){  arrange_for_resumption(handle);  throw oops; // who catches this?}

抛出的异常被coroutine框架捕获,该框架调用.NET Framework。但是promise可能已经不存在了!promise.unhandled_exception()

处理所有这些情况使得x86上的异常处理,特别是x86上的coroutine的异常处理,成为一项相当复杂的工作。

END


新程序员001-004》全面上市,对话世界级大师,报道中国IT行业创新创造


  
  
    
   
   
     
— 推荐阅读 —
    
    
      
☞《程序员延寿指南》登GitHub热榜,最多可增寿20年?
☞开源文化依旧熠熠生辉 —— 在openEuler社区,有技术、有idea,你就是主角!
☞霸榜 GitHub 热门第一多日后,Colossal-AI 正式版发布

点这里↓↓↓记得关注标星哦~ 

一键三连 「分享」「点赞」「在看」

成就一亿技术人

登录查看更多
0

相关内容

ICCV 2021最佳论文出炉!微软Swin Transformer摘得马尔奖
专知会员服务
29+阅读 · 2021年10月13日
专知会员服务
16+阅读 · 2021年9月17日
【干货书】面向计算科学和工程的Python导论,167页pdf
专知会员服务
41+阅读 · 2021年4月7日
【2020新书】懒人程序员专用书C++20,681页pdf
专知会员服务
43+阅读 · 2020年12月15日
【2020新书】现代C++初学者指南,301页pdf
专知会员服务
159+阅读 · 2020年7月24日
Github开发大神教你玩转数据库编程
新智元
0+阅读 · 2022年5月2日
网易数帆云原生日志平台架构实践
专知
1+阅读 · 2022年3月12日
gRPC 与.NET 入门
InfoQ
0+阅读 · 2022年2月17日
微软停止对Visual Studio旧版支持
CSDN
0+阅读 · 2022年2月15日
国家自然科学基金
2+阅读 · 2014年12月31日
国家自然科学基金
1+阅读 · 2013年12月31日
国家自然科学基金
2+阅读 · 2013年12月31日
国家自然科学基金
0+阅读 · 2013年12月31日
国家自然科学基金
0+阅读 · 2013年12月31日
国家自然科学基金
0+阅读 · 2013年12月31日
国家自然科学基金
0+阅读 · 2012年12月31日
国家自然科学基金
2+阅读 · 2012年12月31日
国家自然科学基金
1+阅读 · 2011年12月31日
国家自然科学基金
0+阅读 · 2008年12月31日
How Asynchronous Events Encode Video
Arxiv
0+阅读 · 2022年6月9日
Arxiv
0+阅读 · 2022年6月8日
Arxiv
0+阅读 · 2022年6月1日
VIP会员
相关基金
国家自然科学基金
2+阅读 · 2014年12月31日
国家自然科学基金
1+阅读 · 2013年12月31日
国家自然科学基金
2+阅读 · 2013年12月31日
国家自然科学基金
0+阅读 · 2013年12月31日
国家自然科学基金
0+阅读 · 2013年12月31日
国家自然科学基金
0+阅读 · 2013年12月31日
国家自然科学基金
0+阅读 · 2012年12月31日
国家自然科学基金
2+阅读 · 2012年12月31日
国家自然科学基金
1+阅读 · 2011年12月31日
国家自然科学基金
0+阅读 · 2008年12月31日
Top
微信扫码咨询专知VIP会员