參考資料:韋東山第三期
程序、執行緒、中斷的核心:棧
ARM處理器程式執行的過程:
ARM晶片屬於精簡指令集計算機(RISC:Reduced Instruction Set Computing),它採用的指令比較簡單,有以下特點:
1、對記憶體只有讀寫指令
2、對於資料的運算是在CPU內部實現的
3、使用RISC指令的CPU複雜度小一些,易於設計
以a = a+b為例,操作如下:
程式碼就是機器指令
在有MMU的系統中,每個程序的空間都相互隔離,程序A無法修改程序B的程式碼、資料;在沒有MMU的系統中,比如執行UCOS的微控制器中,無法保證程序A不破壞程序B
只能在CPU切出去瞬間,將暫存器儲存下來,儲存在棧中,儲存的這些值稱為現場
過程如下:
使用棧來儲存現場是所有作業系統程序排程的核心
Linux中:資源分配的最小單位是程序,排程的最小單位是執行緒
也就是說,在一個程序中,可能有多個執行緒,這些執行緒公用開啟的檔案控制代碼,全域性變數等等。而這些執行緒,之間又是互相獨立的,“同時執行”,也就是說:每一個執行緒,都有自己獨立的棧
Linux對中斷的擴充套件:
Linux中,對中斷處理有兩個原則:
1、中斷不能巢狀,也就是說中斷不能被打斷
2、中斷的處理要越快越好
對於耗時的中斷,可將中斷分為上半部和下半部,用核心執行緒來處理中斷
中斷上半部:在關閉中斷下處理緊急的事情,此時無法處理其他中斷
中斷下半部:在開中斷下處理非緊急的事情,此時會被中斷打斷,是軟體中斷實現的
硬體中斷陣列
如果在中斷下半部是再次被中斷,這是第二次打斷也不會被執行,如下:
所以,中斷上半部對於中斷下半部是多對一的關係。
那麼鍵盤的過程中,有可能再次觸發按鍵的中斷,所以需要把所有的按鍵都讀出來,讀完為止
A上半部處理完成,開啟中斷,A進入中斷下半部,此時B程序中斷,這個過程中,A處理的是所有中斷的下半部
下半部耗時如果不是太長:tasklet
下半部耗時長且複雜:workqueue,worker核心執行緒
中斷下半部的執行過程中,雖然是開中斷的,期間可以處理各類中斷。但是畢竟中斷的處理還沒完,這期間APP是無法執行的。所以,如果中斷要做的事情實在太耗時,那就不能用軟體中斷來做,而應該用核心執行緒來做:在中斷上半部喚醒核心執行緒。核心執行緒和APP都一樣競爭執行,APP有機會執行,系統不會卡頓
這個核心執行緒是系統幫忙建立的,一般是worker執行緒,可以用ps -A| grep kworker查詢;
核心有一個work queue,只要把核心下半部函式work放入work queue。這樣整個處理方法
執行緒化的中斷threaded irq:
使用執行緒來處理中斷,並不是什麼新鮮事。使用work就可以實現,但是需要定義work、呼叫schedule_work,很麻煩
核心有相應的函式:
可以只提供thread_fn,系統會為這個函式建立核心執行緒。發生中斷時,核心執行緒就會執行這個函式
每一箇中斷都建立一個核心執行緒
軟體上處理中斷的過程與硬體上中斷髮生的過程剛好是反過來的
兩個中斷號,GIC呈現給CPU的A號中斷,另外一個GPIO模組裡面的B號中斷
request_irq註冊irq,核心就會幫建立一個irqaction結構體,這個結構體,會放有handler函式和執行緒處理函式。對於共享中斷,每個外部裝置的處理函式都會鏈在一起。這些外設的中斷處理函式都會被執行,來確認是哪一個具體裝置中斷觸發的
handler處理函式就是中斷上半部,threaded_fn就是中斷下半部
request_irq(irq, handler)函式中用到的irq,實際上是虛擬中斷號
不同中斷控制器都分配有一個irq_domain,解析interrupt-parent時生成
GPIO控制器裡面有第一號中斷,UART模組裡面也有第一號中斷。這兩個“”第一號中斷”是不一樣的,它們屬於不同的“域”--irq_domain
irq_domain裡面有個.xlate函式用來解析裝置樹,.map函式會將hwirq轉換成虛擬中斷號
核心在處理裝置樹時,會使用irq_domain裡面的map函式,建立hwirq和virq的關係,儲存在linear_revmap
讀取GPIO暫存器得到hwirq,根據hwirq得到之前對映的irq