什麼是中斷
中斷其實是一種“中斷”事件,中斷具體代表什麼意思需要考慮它所處的上下文環境
和參照物件
是誰。考慮事件,我們可以簡單把中斷抽象為這樣一種模型:
當我們分析某種中斷事件時,我們需要搞清楚這四個物件:
中斷源
- 中斷源是誰
- 中斷源在什麼條件下觸發中斷
- 中斷源如何觸發
中斷訊號
- 訊號具體指的是什麼
- 訊號是否需要儲存
- 如何儲存
中斷控制器
- 中斷訊號的管理
比如說中斷源傳送的訊號是否遮蔽,訊號是否可被中斷處理器重複處理,訊號的處理是否有優先順序...
中斷處理器
- 如何獲取到訊號
- 拿到訊號做什麼樣的操作
- 處理完訊號後做什麼樣的操作
在實際的中斷事件中,並不一定剛好有上面提到的這四類物件,可能更復雜可能更簡單化。但是當我們考慮中斷事件時,需要明確應該有類似功能的“物件”承擔這這樣的邏輯。
下面我們主要圍繞作業系統的中斷機制
,Java的中斷機制
,如何設計一個非同步執行緒間的中斷系統
這三部分簡單探討下。
作業系統的中斷機制
與作業系統有關的中斷,通常是指:程式在執行過程中,遇到急需處理的事件時,暫時中止CPU上現行程式的執行, 轉去執行相應的事件處理程式,待處理完成 後再返回原程式被中斷處或排程其他程式執行的過程。
按照中斷事件本身的不同,可以劃分為處理器之外的中斷事件
,異常
,系統異常
。
處理器之外的中斷事件
指由外圍裝置發出的訊號引起的,與當前執行指令無關的中斷事件。示意圖如下:
我們分別以上述四個物件來看:
- 中斷源
中斷源:外部裝置,如印表機,鍵盤,滑鼠等。
觸發條件:如外圍裝置報告I/O狀態的I/O中斷;外圍裝置發出的對應訊號中斷,如時鐘中斷,鍵盤/滑鼠對應訊號的中斷,關機/重啟動中斷等。
觸發方式:由外部裝置向中斷控制器發出中斷請求IRQ。
- 中斷訊號
也就是說中斷源通知給中斷控制器的是什麼。
可以是通過一條訊號線上產生特定的電平(利用高低電平表示是否中斷兩種狀態),也可以在匯流排上傳送特定訊息或者訊息序列,也可以是在中斷暫存器中設定已發生的中斷狀態等。
- 中斷控制器
CPU中的一個控制部件,包括 中斷控制邏輯線路和中斷暫存器。負責中斷的發現和響應。
也就是說負責檢查中斷暫存器中的中斷訊號,當發現中斷時讓CPU切換當前程式程式,去處理中斷程式。響應示意圖如下:
- 中斷處理器
指的是CPU接收到不同的中斷訊號該怎麼處理。包括“中斷處理過程”和“恢復正常操作”兩部分。
1.中斷處理過程
首先CPU需要將當前執行程式的上下文儲存,從中斷程式中分析PSW,確定對應的中斷源和執行對應的中斷處理程式。
小貼士
:PSW(Program Status Word): 是指在電腦中,一段包含被作業系統使用的程式狀態資訊的記憶體或硬體區域。一般用一個專門的暫存器來指示處理器狀態。可以理解為我們上面提到的中斷訊號儲存裝置.
2.恢復正常操作
當中斷程式執行完畢,接下來執行哪個程式由程式排程決定,由排程策略決定是否排程到中斷執行前的程式。
較為完整的中斷響應流程圖如下:
異常 和 系統異常 這兩類中斷事件主要屬於處理器執行特定的指令引起的中斷事件。和上述硬體外圍裝置引起的中斷事件的中斷源不同,中斷的發起,控制和處理主要是由作業系統的指令邏輯和線路來承擔。是一種同步的處理操作,而外部中斷是由外部裝置發起,是一種非同步的處理操作。下面我們簡要介紹下。
異常
異常指當前執行指令引起的中斷事件。包括錯誤情況引起的故障,如除零算數錯誤,缺頁異常;也包括不可恢復的致命錯誤導致的終止,通常是一些硬體錯誤。
- 異常的處理
對於故障
的處理,根據故障是否能夠被恢復,故障處理程式要麼重新執行引起故障的指令,要麼終止。
對於終止
的處理,處理程式將控制返回給一個abort例程,該例程會終止這個應用程式。
系統異常
系統異常指執行陷入指令而觸發系統呼叫引起的中斷事件,如請求裝置、請求I/O、建立程式等。
- 系統呼叫的處理
這種有意的異常,稱為陷阱處理。處理完成後陷阱程式會將控制返回給應用程式控制流的下一條指令。
總結一下,作業系統的中斷類別行為如下:
好了,大頭總算完了。因為小姐姐主要是Java碼農,下面將主要介紹和Java相關的中斷語義是什麼。
Java的中斷機制
理解了上面作業系統的中斷之後,Java的中斷機制就很easy了 ?
Java中斷指的是A執行緒傳送中斷訊號給B執行緒,B執行緒再根據自己當前執行程式中的中斷處理邏輯決定如何響應。嗯,就這麼簡單~
我們來稍微分析一下中斷事件中的“四個物件”:
- 中斷源
中斷源:A執行緒
中斷觸發條件:A執行緒說了算
中斷源觸發方式:A執行緒中呼叫threadB#interrupt()
方法.
實現機制也不難,扯淡之前我們先思考兩個問題:
問:
問題1
: 執行緒之間如何通訊,A執行緒的中斷訊號怎麼才能傳給執行緒B?
問題2
: 執行緒的狀態有Running,Blocked,Waiting等,當執行緒B處在不同的狀態下,如何響應中斷訊號?
答:
問題1
:這種情況下執行緒之間通訊用共享記憶體就可以了。只需要給每個執行緒都設定一個中斷標示位, 這樣A執行緒中呼叫threadB#interrupt()
方法,實際操作是把B執行緒的中斷標示位設定為true。訊號就算傳遞過去了
問題2
:當B執行緒處於非阻塞狀態時,B執行緒可以在自己需要處理中斷邏輯的地方判斷中斷標示位是否為true,就可以響應處理中斷。
但是當B執行緒處於阻塞狀態時,這特麼怎麼查自己的中斷標示位啊?
JVM幫幫忙,當B執行緒阻塞在Object#wait()
,Thread#join()
,Thread#sleep()
,實現了InterruptibleChannel
介面的IO操作 和Selector
介面的select()
這些操作時,JVM會讓B執行緒馬上丟擲異常或被喚醒,從而讓B執行緒可以選擇是否響應中斷。
因為是Java實現的中斷機制,中斷標示位的設定也是JVM幫做的。
- 中斷訊號
訊號:執行緒的中斷標示位。
儲存方式:JVM說了算。
- 中斷控制器
JVM控制了訊號的儲存和讓執行緒B及時喚醒。
執行緒B控制了自己的中斷響應邏輯,何時響應,如何響應。
- 中斷處理器
獲取訊號:B執行緒可通過呼叫threadB#isInterrupted()
方法得知自己是否被中斷,也就是通過自己主動拉取訊號(poll方式)。
如何處理訊號:B執行緒說了算。
處理完訊號後做什麼:B執行緒說了算。
Java的執行緒中斷機制設計的比較靈活,使用者可以決定中斷處理的較多事情。
總結下Java中和中斷有關的方法:
在JDK中,執行緒池的ThreadPoolExecutor#shutdownNow()
方法就是呼叫workers執行緒陣列中每個worker執行緒的interrupt()
方法來關閉執行緒池。
這樣暴力關閉執行緒會存在一個問題,執行緒池並不知道worker執行緒的中斷執行情況,如果worker執行緒忽略了中斷訊號,那可能導致當前任務還在執行,發生意想不到的結果。
設計一個非同步執行緒間的中斷系統
我們再來看Java的中斷機制,它其實只是提供了A執行緒給B執行緒傳送中斷訊號。
- A執行緒並不能知道B執行緒的中斷處理結果。
- 如果A執行緒拿不到B執行緒的thread物件時,也就沒法傳送中斷訊號。
考慮這麼一種場景:
當我們執行一個大任務Task1
時,它太大了。我們把它分為Task2
和Task3
,丟進執行緒池中處理。它們同樣很大,我們把他們分別分為Task4
,Task5
和Task6
,Task7
,同樣丟進執行緒池中處理。
如果此時我們想取消task1的執行,如何保證圖中所有的worker都成功取消對應task的執行?
- 需求分析
當我們取消task1時,想要做的是取消所有task程式的繼續執行
,並且能夠獲得所有task程式的取消結果
。
為什麼要強調task程式呢?因為worker可能並不是只為一個task工作啊..比如task2的worker,它把task4和task5丟進執行緒池,就算完事了。如果我們把取消task1變為取消task1的worker執行緒,可能會導致worker執行緒當前執行的非task1程式的失敗。
我們不太容易知道所有task程式當前執行的執行緒,我們還需要知道所有task程式的執行結果。
- 設計思路
只用Java的中斷機制是滿足不了我們的需求的,但是我們可以借鑑它的思路:
1.它用中斷標示位記錄執行緒是否應該中斷
2.當執行緒阻塞時可以丟擲異常
我們這裡要終止的是所有task程式的執行,所以我們需要設計與task 強繫結的中斷標示位
,可以有未中斷
,中斷中
,中斷成功
,中斷失敗
四種 狀態。為了讓所有的執行緒都可以訪問到,定義成全域性共享變數就可以。
中斷源和中斷處理器之間通過task的中斷標示位來通訊就可以。如果執行task程式的執行緒一直在阻塞,怎麼喚醒它讓它判斷中斷狀態 呢?
對於我們這個場景,我們很難知道當前執行task程式的阻塞執行緒是誰。。能做的只是多安插中斷判斷點,這樣當阻塞執行緒醒來後,再次判斷task 的中斷標示位,就可以響應中斷了。
另:
喚醒一個執行緒只有Java的中斷機制可以做,但是如果當前worker不是你能管理的執行緒池,那麼它的中斷處理邏輯就控制不了。
如果你能控制執行task的所有worker,而且worker在執行task時是同步獲得結果的。那麼可以結合與task強繫結的中斷標示位
和Java中斷機制
來做,這裡前者的作用更多是充當獲取到任務的中斷結果的作用。
後記
-
小姐姐覺得像是“事件處理”這種場景線上程池,訊息中介軟體,流式處理等很多地方有共通之處,比如說:如何保證事件的exactly once,推拉模型,排程等等。
-
在寫這篇文章時,特別是作業系統的中斷機制,小姐姐也是現學現賣,並且參考了資料大部分內容。文章中有理解錯誤或者難懂的地方還請小夥伴幫我指出,一起交流進步。
-
最後的技術部分討論“如何設計一個非同步執行緒間的中斷系統”,這是小姐姐目前工作中遇到的一個問題。這個問題和任務排程元件的取消任務很相似,只是我們目前還沒有用任務排程元件管理起所有的任務工作執行緒。小夥伴有更好方案的也請告知小姐姐。
最後,聊一件生活中的小事~
近來小姐姐發現了B站的一個寶藏優(U)批(P)主:天真的和感傷的小說家。最近的更新視訊他聊到了餘華的《活著》,原來當年讓小姐姐哭的稀里嘩啦的著作裡面還有這麼一個段子啊,模仿著說給你聽 ?
一一進來了順手點了個關注
。22君 看完忍不住送了個在看
,33醬 覺得這麼讚的小姐姐怎麼著也得幫分享
到朋友圈吧。66君醬 我看到你讚賞
了,來就來了還那麼客氣幹嘛~
證明你不是一個人,一連二連三連全連一下下,救救小姐姐吧:)
參考資料:
[1].https://www.icourse163.org/course/NJU-1001571004
[2].《深入理解計算機系統》
[3].https://www.zhihu.com/question/47862508/answer/110694813