【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翻譯,轉載需註明來源出處。
以下為譯文:
最近,我發現x86架構還有一個和其他的架構不同的地方——Windows結構化異常的管理方式。
在Windows上,所有其他架構都是通過使用unwind代碼和聲明為元數據的其他信息來跟蹤異常處理。如果在其他架構上單步執行一個函數,就不會看到與異常處理相關的任何指令。只有在發生異常時,系統才會在元數據中的異常處理信息中查找指令指針,並使用它來決定要執行的操作:應該運行哪個異常處理程序?哪些對象需要銷毀?和其他諸如此類的問題。
但奇怪的是,在Windows上,x86在運行時跟蹤異常信息。當控制進入一個需要處理異常的函數時(要麼是因為它想要處理異常,要麼只是因為它想在異常被拋出函數時運行析構函數),代碼必須在一個通過堆棧的鏈接列表中創建一個條目,並以.NET中的值為錨。在Microsoft Visual C++的實現中,鏈接列表節點還包含一個整數,代表當前函數的進度,每當需要銷毀的對象列表發生變化時,該整數就會被更新。它在一個對象的構建完成後立即更新,並在對象的銷毀開始前立即更新。fs:[0]
這個特殊的整數是一個非常麻煩的問題,因為優化器視其為廢棄儲存,想把它優化掉。的確,有時它確實是廢棄儲存,但有時它不是。
此函數的代碼生成過程如下:
每當 "需要銷毀的對象 "的列表發生變化時,就會更新unwind狀態變量。就優化器而言,所有這些更新看起來都是廢棄儲存,因為似乎沒有人讀它們。.state
但確實有人讀它們:.the。問題是,對the的調用是不可見的。當一個異常被或函數拋出時,它被調用,或者被對象的析構器調用。
但是,其中有些真的是廢棄儲存。例如,2的賦值是一個廢棄儲存,因為它後面緊跟着3的存儲,中間沒有任何東西,所以當值是2的時候,不會有異常發生。同樣,3的存儲是廢棄的,因為3的析構器是隱含的。當破壞.node.stateSnoexcepts1時,不可能發生異常。
如果或改為.f1f2noexcept,廢棄儲存就可能被消除。
因此,優化器進退兩難。它想消除廢棄儲存,但識別廢棄儲存的簡單算法在這裡不起作用,因為有可能出現異常。
Coroutine使情況變得更糟:當一個coroutine暫停時,異常處理節點需要從堆棧中複製到coroutine框架中,然後從堆棧框架中刪除。而當協程恢復時,狀態需要從協程框架複製回堆棧,並鏈接到異常處理程序鏈中。
確切地知道何時執行此操作取消鏈接和重新鏈接是很困難的,因為你仍然必須捕獲其中發生的異常,並把它們存儲在promise中。但這很可能不可行,因為在返回之前,coroutine可能已經恢復並運行到完成。
.await_suspendawait_suspendawait_suspend
拋出的異常被coroutine框架捕獲,該框架調用.NET Framework。但是promise可能已經不存在了!promise.unhandled_exception()
處理所有這些情況使得x86上的異常處理,特別是x86上的coroutine的異常處理,成為一項相當複雜的工作。

END
《新程序員001-004》全面上市,對話世界級大師,報道中國IT行業創新創造
—點這裡↓↓↓記得關註標星哦~—
一鍵三連 「分享」「點讚」「在看」
成就一億技術人
