無缺的程序都知足如下特徵html
咱們的程序和指令都是一條條順序執行,不須要經過鍵盤或者網絡給這個程序任何輸入前端
沒有遇到計算溢出之類的程序錯誤。java
不過,現實的軟件世界可沒有這麼簡單程序員
遇到這些狀況,計算機是怎麼運轉的呢,也就是說,計算機到底是如何處理異常的segmentfault
一提到異常 (Exception),可能你的第一反應就是Java中的Exception。 不過咱們今天講的,並非這些軟件開發過程當中遇到的「軟件異常」
而是和硬件、系統相關 的「硬件異常」。後端
固然,「軟件異常」和「硬件異常」並非業界使用的專有名詞,只是我爲了方便給你說明,和Java中軟件拋出的Exception進行的人爲區分,你明白這個意思就好。網絡
儘管,這裏我把這些硬件和系統相關的異常,叫做「硬件異常」。可是,實際上,這些異常,既有來自硬件的,也有來自軟件層面的。前後端分離
好比,咱們在異步
當加法器進行兩個數相加的時候,會遇到算術溢出
或者,你在玩遊戲的時候,按下鍵盤發送了一個信號給到CPU,CPU要去執行一個現有流程以外的指令,這也是 一個「異常」ide
一樣,來自
好比咱們的程序進行系統調用,發起一個讀文件的請求。這樣應用程序向系統調用發起請求的狀況,同樣是經過「異常」來實現的。
異常, 實際上是一個硬件和軟件組合到一塊兒的處理過程。
異常的發生和捕捉,在硬件層面完成
異常的處理,實際上是由軟件來完成的!
計算機會爲每一種可能會發生的異常,分配一個異常代碼(Exception Number)
異常代碼也叫做中斷向量(Interrupt Vector)。
異常發生的時候,一般是CPU檢測到了一個特殊的信號。
好比
這些信號呢,在組成原理,通常叫發生了一個事件(Event)
CPU在檢測到事件的時候,其實也就拿到了對應的異常代碼。
這些異常代碼裏
拿到異常代碼以後,CPU就會觸發異常處理的流程
計算機在內存裏,會保留一個異常表 (Exception Table)。
也叫中斷向量表(Interrupt Vector Table),好和上面的中斷向量對應起來。
這個異常表有點兒像咱們在以前的GOT表,存放的是不一樣的異常代碼對應的異常處理程序(Exception Handler)所在的地址
咱們的CPU在拿到了異常碼後
這樣「檢測異常 => 拿到異常碼 => 再根據異常碼進行查表處理」的模式,在平常開發的過程當中是很常 見的。
flowchat st=>start: 開始 e=>end: 結束 op1=>operation: 檢測異常 op2=>operation: 拿到異常碼 op3=>operation: 再根據異常碼進行查表處理 st->op1->op2->op3 op3->e
好比說
一般都是先後端分離的
前端的應用根據這個錯誤代碼,
能夠設定ExceptionHandler,來處理線程執行中的異常狀況
public class LastChanceHandler implements Thread.UncaughtExceptionHandler { @Override public void uncaughtException(Thread t, Throwable e) { // do something here - log to file and upload to // server/close resources/delete files... } } Thread.setDefaultUncaughtExceptionHandler(new LastChanceHandler());
能夠指定一個異常處理程序。
對於各個線程在執行任務出現的異常狀況,咱們是經過異常處理程序進行處理,而不是在實際的任務代碼裏處理。
這樣,咱們就把業務處理代碼就和異常處理代碼的流程分開了。
異常能夠由硬件觸發,也能夠由軟件觸發
顧名思義,就是程序在執行到一半的時候,被打斷了。這個打斷執行的信號,來自於CPU外部的I/O設備。
你在鍵盤上按下一個按鍵,就會對應觸發一個 相應的信號到達CPU裏面。CPU裏面某個開關的值發生了變化,也就觸發了一箇中斷類型的異常。
程序員「故意「主動觸發的異常。就好像你在程序裏面打了一個斷點,這個斷點就是設下的一個"陷阱"。
當程序的指令執行到這個位置的時候,就掉到了這個陷阱當中。而後,對應的異常處理程序就會來處理這個"陷阱"當中的獵物。
最多見的一類陷阱,應用程序調用系統調用的時候,也就是從用戶態切換到內核態的時候。
能夠用Linux下的time指令,去查看一個程序運行實際花費的時間,裏面有在用戶態花費的時間(user time),也有在內核態發生的時間 (system time)。
應用程序經過系統調用去讀取文件、建立進程,其實也是經過觸發一次陷阱來進行的。這是由於用戶態的應用程序沒有權限來作這些事情,須要把對應的流程轉交給有權限的異常處理程序來進行。
陷阱是咱們開發程序的時候刻意觸發的異常,而故障一般不是。
好比,咱們在程序執行的過程當中,進行加法計算髮生了溢出,其實就是故障類型的異常。
這個異常不是咱們在開發的時候計劃內的,也同樣須要有對應的異常處理程序去處理。
故障在異常程序處理完成以後,仍然回來處理當前的指 令,而不是去執行程序中的下一條指令。
由於當前的指令由於故障的緣由並無成功執行完成。
與其說這是一種異常類型,不如說這是故障的一種特殊狀況。 當CPU遇到了故障,可是恢復不過來的時候,程序就不得不停止了。
中斷異常的信號來自系統外部,而不是在程序本身執行的過程當中,因此咱們稱之爲「異步」類型的異常。
而陷阱、故障以及停止類型的異常,是在程序執行的過程當中發生的,所 以咱們稱之爲「同步「類型的異常。
在處理異常的過程中,不管是異步的中斷,仍是同步的陷阱和故障,咱們都是採用同一套處理流程,也就是上面所說的,「保存現場、異常代碼查詢、異常處理程序調用「。
而停止類型的異常,實際上是在故障類型異常的一種特殊狀況。當故障發生,可是咱們發現沒有異常處理程序可以處理這種異常的狀況下,程序就不得不進入停止狀態,也就是最終會退出當前的程序執行。
在實際的異常處理程序執行以前,CPU須要去作一次「保存現場」的操做。這個保存現場的操做, 和函數調用的過程很是類似。
切換到異常處理程序,就好像是去調用一個異常處理函數。指令的控制權被切換到了另一個"函數",因此咱們天然要把當前正在執行的指令去壓棧。
這樣才能在異常處理程序執行完後,從新回到當前的指令繼續往下執行。
不過,切換到異常處理程序,比起函數調用,仍是要更復雜一些。緣由有下面幾點
因此,對於異常這樣的處理流程,不像是順序執行的指令間的函數調用關係。而是更像兩個不一樣的獨立進程之間在CPU層面的切換,因此這個過程咱們稱之爲上下文切換(Context Switch)。
計算機裏的「異常」處理流程。這裏的異常能夠分紅中斷、陷阱、故障、停止 這樣四種狀況。這四種異常,分別對應着I/O設備的輸入、程序主動觸發的狀態切換、異常狀況下的程序出錯以及出錯以後無可挽回的退出程序。
當CPU遭遇了異常的時候,計算機就須要有相應的應對措施。CPU會經過「查表法」來解決這個問 題。在硬件層面和操做系統層面,各自定義了全部CPU可能會遇到的異常代碼,而且經過這個異 常代碼,在異常表裏面查詢相應的異常處理程序。在捕捉異常的時候,咱們的硬件CPU在進行相 應的操做,而在處理異常層面,則是由做爲軟件的異常處理程序進行相應的操做。
而在實際處理異常以前,計算機須要先去作一個「保留現場」的操做。有了這個操做,咱們才能在異常處理完成以後,從新回到以前執行的指令序列裏面來。這個保留現場的操做,和咱們以前講 解指令的函數調用很像。可是,由於「異常」和函數調用有一個很大的不一樣,那就是它的發生時間。函數調用的壓棧操做咱們在寫程序的時候徹底可以知道,而「異常」發生的時間卻很不肯定。 因此,「異常」發生的時候,咱們稱之爲發生了一次「上下文切換」(Context Switch)。這個時 候,除了普通須要壓棧的數據外,計算機還須要把全部寄存器信息都存儲到棧裏面去。
關於異常和中斷,《深刻理解計算機系統》的第8章「異常控制流」部分,有很是深刻和充分的講解,推薦你認真閱讀一下。
不少教科書和網上的文章,會把中斷分紅軟中斷和硬中斷。你能用本身的話說一說,什麼是軟中 斷,什麼是硬中斷嗎?它們和咱們今天說的中斷、陷阱、故障以及停止又有什麼關係呢?
歡迎留言和我分享你的疑惑和看法。你也能夠把今天的內容,分享給你的朋友,和他一塊兒學習和 進步。
https://www.cnblogs.com/luoah...
深刻理解計算機系統(第三版)
本文由博客一文多發平臺 OpenWrite 發佈!