2012-02-05 155 views
1

幾個月來,我一直在研究「自制」操作系統。 目前,它啓動並進入32位保護模式。 我已經加載了中斷表,但還沒有設置分頁(還)。操作系統開發:如何避免例外程序後的無限循環

現在在編寫我的異常例程時,我注意到當一條指令拋出一個異常時,異常例程就會執行,但是CPU會跳回到引發異常的指令!這並不適用於每一個異常(例如,一個div零異常會跳回指令除法指令之後),但是讓我們考慮下面的一般保護性異常:

MOV EAX, 0x8 
MOV CS, EAX 

我的例行很簡單:調用顯示紅色錯誤消息的函數。

結果:MOV CS,EAX失敗 - >我的錯誤消息顯示 - > CPU跳回到MOV CS - >無限循環垃圾郵件錯誤消息。

我在操作系統和unix安全方面與老師討論過這個問題。 他告訴我他知道Linux有辦法繞過它,但他不知道哪一個。

幼稚的解決方案是從例程中解析拋出指令,以獲得該指令的長度。 該解決方案非常複雜,我感到有點不舒服,在每個受影響的例外程序中增加一個相對較重的功能的呼叫...

因此,我想知道是否是解決問題的另一種方法。也許有一個「神奇」的寄存器,包含一點可以改變這種行爲?

-

非常感謝您提前任何建議/信息。

-

編輯:好像很多人想知道爲什麼我想跳過有問題的指示,恢復正常運行。

我有兩個方面的原因:

  1. 首先,殺的過程將是一個可能的解決方案,而不是一個乾淨的。這不是它在Linux中的做法,例如,內核發送信號(我認爲是SIGSEGV),但不會立即中斷執行。這很有意義,因爲應用程序可以阻止或忽略信號並恢復自己的執行。這是一個非常優雅的方式來告訴應用程序它做了錯誤的國際海事組織。

  2. 另一個原因:如果內核本身執行非法操作會怎麼樣?可能是由於一個錯誤,但也可能是由於內核擴展。正如我在評論中所說:在這種情況下我應該怎麼做?我只需要殺死內核並用笑臉顯示一個漂亮的藍屏?

這就是爲什麼我希望能夠跳過指令。 「猜測」指令的大小顯然不是一種選擇,解析指令看起來相當複雜(不是我介意實現這樣的例程,但我需要確保沒有更好的方法)。

回答

2

不同的例外有不同的原因。一些例外是正常的,並且該例外僅在軟件繼續運行之前告訴內核它需要做什麼。例如,頁面錯誤告訴內核它需要從交換空間加載數據,一個未定義的指令異常告訴內核它需要模擬一個CPU不支持的指令,或者一個調試/斷點異常告訴內核它需要通知調試器。對於這些內核來說,修正事情並默默繼續是正常的。

某些異常表示異常情況(例如軟件崩潰)。處理這類異常的唯一理由是停止運行軟件。您可以保存信息(例如核心轉儲)或顯示信息(例如「藍屏死機」)以幫助進行調試,但最終軟件會停止(程序終止,或內核進入「不做任何事情直到用戶重置計算機「狀態)。

忽略異常情況只會讓人們很難找出問題所在。例如,假設指令去廁所:

  • 進入浴室
  • 刪除褲子
  • 開始產生輸出

現在想象一下,第2步失敗,因爲你穿短褲(一種「找不到褲子」的例外)。你是否想在那個時候停下來(有一個很容易理解的錯誤信息或者其他的東西),或者忽略那一步,並且在所有有用的診斷信息都消失之後,試圖找出以後出錯的地方?

+0

我明白你的觀點。我知道繼續執行產生段錯誤的進程是沒有意義的,但我只是想*做到這一點,主要是出於好奇。 從我目前所瞭解的情況來看,不可能以簡單的方式來完成(例如,沒有定義中斷行爲的神奇寄存器)。 – 2012-02-08 15:05:35

+1

如果你確實想要做到這一點,那麼你必須實現一個解碼器來確定RAM中原始字節的指令長度。對於80x86(可變長度指令,前綴等)這將是一大筆工作。一旦你得到了它的「工作」,它在某些情況下仍然不起作用。例如,想象一下由「頁面不存在」導致的頁面錯誤 - 您無法嘗試解碼不存在的字節。 – Brendan 2012-02-08 15:18:28

+0

我知道這將是一個大量的工作。沒有想到頁面錯誤,但這不是一個很大的問題(至少在我的情況下,還沒有)。 – 2012-02-08 15:47:03

2

如果我理解正確,您希望跳過導致異常的指令(例如mov cs, eax),並在下一條指令中繼續執行程序。

你爲什麼要這麼做?通常,程序的其他部分不應該依賴於成功執行該指令的效果嗎?

一般來說,有三種方法的異常處理:

  • 對待異常作爲不可修復的條件和終止進程。例如,零除以通常以這種方式處理。

  • 修復環境,然後再次執行指令。例如,頁面錯誤有時以這種方式處理。

  • 使用軟件模擬指令並在指令流中跳過指令。例如,有時以這種方式處理複雜的算術指令。

+0

如果指令出現在一個進程中,那麼殺死進程將是最簡單的解決方案。但是,如果指令正確顯示在內核中呢?不一定是基本的內核代碼,也可能是內核擴展......我不能殺死內核! (我沒有試圖重新創建Windows);)我不確定我是否理解了關於軟件仿真的第三點意味着什麼,但它似乎比簡單解碼指令更復雜! – 2012-02-05 18:29:48

+0

關於仿真的第三點是針對類似CPU不支持浮點的情況,但您希望在軟件中模擬這些指令。 – Nayuki 2012-02-05 18:45:37

+0

你能回答這些問題嗎? 「你爲什麼要這樣做?通常,程序的其他部分不應該依賴於成功執行該指令的效果嗎?」 – Nayuki 2012-02-05 18:48:02

2

你所看到的是一般保護例外的特徵。英特爾系統編程指南中明確指出(6.15異常和中斷參考/中斷13 - 一般保護性異常(#GP)):

Saved Instruction Pointer 
The saved contents of CS and EIP registers point to the instruction that generated the 
exception. 

因此,你需要編寫一個異常處理程序將跳過該指令(這可能有些奇怪),或者只是簡單地用「一般保護例外$SAVED_EIP」或類似消息來終止違規流程。

0

我可以想象出一些情況,其中一個人想要通過解析失敗的指令,模擬其操作,然後返回指令來響應GPF。正常模式是設置事件,以便指令(如果重試)能夠成功,但可以例如有一些代碼需要訪問地址爲0x000A0000-0x000AFFFF的硬件,並希望在缺少這種硬件的機器上運行它。在這種情況下,人們可能不希望在該空間的「真實」內存中存儲資金,因爲每一次訪問都必須單獨進行處理和處理。我不確定是否有任何方法可以解決這個問題,而不必解碼任何指令試圖訪問內存,儘管我知道一些虛擬PC程序似乎管理得很好。

否則,我建議你應該爲每個線程設置一個跳轉向量,當系統遇到GPF時應該使用該跳轉向量。通常情況下,該向量應該指向一個線程退出例程,但是要使用指針做一些「可疑」事情的代碼可以將其設置爲適合該代碼的錯誤處理程序(代碼應該在向該區域錯誤處理程序本來是合適的)。

我可以想象在哪裏可能想要模仿一個指令而不執行它的情況,以及可能想要將控制權轉移給錯誤處理程序例程的情況,但我無法想象任何人想要跳過的地方通過一條可能導致GPF的指令。

+0

最後一段的+1。 – Nayuki 2012-02-06 03:36:56